Ejb Jsf Struts Flex Jasper

439

Click here to load reader

Transcript of Ejb Jsf Struts Flex Jasper

Page 1: Ejb Jsf Struts Flex Jasper

Ce livre sur les EJB 3 s’adresse aux développeurs Java d’applications web travaillant sur les frameworks Struts 2, JSF 2 ou Flex 3. Le débutant comme l’expert trouveront les informations qui leur conviennent sur l'utilisation des EJB (Enterprise JavaBeans) de manière générale et les gains de productivité apportés par la version 3. L’auteur propose le développement avec les EJB de trois applications web de vente en ligne aux fonctionnalités quasi identiques et qui sont basées sur des couches métier et persistance communes. A l'aide de l'IDE Eclipse et du serveur d'application JBoss 6, il exploite les fonctionnalités d'un container EJB pour : - mettre en place une couche de persistance basée sur les Entity beans, le langage JPQL et la Java Persistence API, - créer des objets métiers à l'aide des Session beans et des Message-driven beans, - définir une politique de sécurité avec une gestion des rôles et des permissions définie dans un fichier de propriétés, une base ou un annuaire LDAP, - exposer des EJB 3 en tant que web services, - mettre en place des traitements différés et ponctuels à l'aide des EJB Timers, - faire de la programmation par aspect grâce aux Interceptors. Tout au long des chapitres, l’auteur : - décrit et met en place les nouveautés incluses dans les dernières versions des frameworks Struts 2 et JSF 2. - détaille l'utilisation du framework GraniteDS pour réaliser la communication entre les objets Java et Flex 3 et créer une interface RIA. met en avant le framework open-source de reporting JasperReports et montre son utilisation avec les EJB, Struts 2 et JSF 2 pour créer des rapports graphiques. Enfin, l’auteur décrit les apports de la toute dernière version des EJB, la version 3.1, qui a été finalisée en décembre 2009. Les sources des applications sont en téléchargement sur le site www.editions-eni.fr et l’auteur continuera de les faire évoluer sur son site. Les chapitres du livre : Avant-propos – Introduction – L’application VenteEnLigne – Les entity beans et l’API de persistance (JPA) – Les session beans – Traitement des commandes avec les Message-Driven Beans – Utilisation des Web Services – Les EJB Timers – Les interceptors– Sécurité – Struts 2 – Développement d’un client avec JSF 2 - Génération de rapports avec JasperReports – Développement d’un client avec Flex 3 – Mise en place de l’environnement – EJB 3.1 : les nouveautés avec Java EE 6

Ce livre numérique a été conçu et est diffusé dans le respect des droits d’auteur. Toutes les marques citées ont été déposées par leur éditeur respectif. La loi du 11 Mars 1957 n’autorisant aux termes des alinéas 2 et 3 de l’article 41, d’une part, que les “copies ou reproductions strictement réservées à l’usage privé du copiste et non destinées à une utilisation collective”, et, d’autre part, que les analyses et les courtes citations dans un but d’exemple et d’illustration, “toute représentation ou reproduction intégrale, ou partielle, faite sans le consentement de l’auteur ou de ses ayants droit ou ayant cause, est illicite” (alinéa 1er de l’article 40). Cette représentation ou reproduction, par quelque procédé que ce soit, constituerait donc une contrefaçon sanctionnée par les articles 425 et suivants du Code Pénal. Copyright Editions ENI

Ce livre numérique intègre plusieurs mesures de protection dont un marquage lié à votre identifiant visible sur les principales images.

Les EJB 3 3 applications détaillées

Celinio FERNANDES

Résumé

L'auteur

Celinio Fernandes est architecte spécialisé dans les technologies Java EE dans une SSII et intervient sur des projets en tant qu'architecte, référent technique et développeur. Il est titulaire de 4 certifications Java EE : SCBCD 5 (EJB 3.0), SCBCD 1.3 (EJB 2.0), SCWCD 1.4 et SCJP 1.4. Il est par ailleurs membre de l'équipe Java de Developpez.com et modérateur des forums.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjqvQgFhH82ICwA=-enidentnumber

Page 2: Ejb Jsf Struts Flex Jasper

Objectifs

Dans cet ouvrage, j’ai voulu montrer que les Enterprise JavaBeans (EJB) 3, souvent décriées dans les premières versions pour leur lourdeur (tant dans leur apprentissage que dans leur mise en place), représentent aujourd’hui une solution à la fois viable, simple et performante pour le développement d’une application client/serveur.

À la fin des années 90, début des années 2000, les EJB représentaient l’une des rares solutions permettant au développeur de déléguer la gestion de la persistance (au container EJB). Puis des frameworks tels qu’Hibernate et Spring ont émergé et proposé des alternatives plus riches et plus simples, notamment au niveau de la gestion de la persistance ou des ressources. Fort heureusement la version 3 des EJB a su prendre en compte certaines des avancées introduites par ces frameworks.

De fait, aujourd’hui, les EJB 3 continuent à s’inscrire de façon logique dans la liste des technologies pérennes qui peuvent :

être utilisées dans les solutions élaborées pour la création de prototypes pour des POC (Proof Of Concept),

être intégrées dans des réponses à des appels d’offre,

servir à l’élaboration d’une solution complète dans un environnement complexe par la diversité et la multitude des besoins techniques et fonctionnels à satisfaire.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MvFP5A9hH82ICwA=-enidentnumber

Page 3: Ejb Jsf Struts Flex Jasper

À qui s’adresse ce livre ?

Ce livre s’adresse aux étudiants et aux développeurs (débutants, intermédiaires ou confirmés) qui souhaitent découvrir ou mieux connaître les EJB mais aussi Struts 2, JSF 2, Flex 3 et JasperReports.

Il s’adresse également aux chefs de projets et architectes ainsi qu’aux responsables techniques qui désirent mettre en place une architecture Java EE qui réponde aux besoins classiques d’une application web d’entreprise : interaction avec une base de données, sécurisation des accès, reporting, gestion des utilisateurs, traitements différés, développement en couches, adoption de design patterns…

Concernant les pré­requis, des connaissances des bases du langage Java sont nécessaires pour aborder ce livre. Une connaissance de JBoss est utile.

Les sources de l’application sont téléchargeables sur le site des Éditions ENI à l’adresse http://www.editions­eni.fr

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqI1/BdhH82ICwA=-enidentnumber

Page 4: Ejb Jsf Struts Flex Jasper

Introduction

Les Enterprise JavaBeans sont une des API qui font partie du standard de développement d’applications d’entreprises Java EE. L’un des plus importants changements depuis Java EE 5 est l’introduction des EJB 3 qui sont devenus des POJO (Plain Old Java Object) basés sur des annotations. Ceci élimine le besoin d’étendre certaines interfaces et de créer des fichiers de configuration XML.

Les EJB 3 intègrent la JPA (Java Persistance API) qui est l’interface standard de mapping orienté objet de la plate­forme Java EE 5. L’API JPA est utilisée au niveau de la couche de persistance pour créer une séparation entre l’application et la base de données.

La JPA est une spécification qui est implémentée par divers vendeurs tels que :

Hibernate, l’implémentation la plus populaire (et qui existait en fait avant la spécification JPA) et également utilisée par JBoss.

Oracle Toplink

EclipseLink

Apache OpenJPA

Kodo

1. Historique des spécifications

Les EJB sont apparus dans leur première version (1.0) en 1997 à la conférence JavaOne de San Francisco.

En 1999, la version 1.1 (J2EE 1.2) rend le support des entity beans obligatoire.

En 2001, la version 2.0 (J2EE 1.3) introduit les Message­Driven Beans, les entity beans 2.x et EJB QL, les interfaces locales et distantes.

La version 2.1 (J2EE 1.4) naît deux ans plus tard, en 2003 : apparition des EJB Timer Service, support pour les Web services et améliorations mineures d’EJB QL.

En 2006, des changements majeurs sont apportés dans la version 3.0 (Java EE 5) pour faciliter le développement : annotations, mécanisme d’injection, spécifications JPA. Ces changements constituent un grand pas en avant et vont être examinés en détails dans ce livre.

Enfin, en décembre 2009, la version 3.1 est finalisée. L’idée est d’aller vers encore plus de simplification.

Au moment de la rédaction de cet ouvrage, la version 3.1 (Java EE 6) n’est implémentée que par Glassfish qui est le serveur d’applications Open Source de Sun et donc forcément le premier serveur à implémenter les nouvelles normes.

À titre d’exemple, voici les dates de sortie de quelques serveurs EJB compatibles et implémentant Java EE 5 :

JBoss 5 : septembre 2008

Oracle Weblogic Server 10.0 : avril 2007

IBM Websphere 7 : septembre 2008

Jonas 5.1 : mars 2009

La barre de planning suivante montre l’historique des EJB :

Le serveur JBoss 6.0 est disponible depuis décembre 2009 mais n’est pas encore certifié Java EE 6. La version des EJB utilisée dans cet ouvrage est la version 3.0. Cependant le dernier chapitre aborde en détails les quelques améliorations et nouveautés que les EJB 3.1 apportent.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mg7UTC5hH82ICwA=-enidentnumber

Page 5: Ejb Jsf Struts Flex Jasper

La plate­forme Java est passée de J2EE 4 à Java EE 5. Le 2 a disparu au profit d’une appellation plus simple : Java Enterprise Edition (Java EE).

2. Comparaison EJB 2 ­ EJB 3

Les spécifications des EJB 3 (JSR 220 (Java Specification Request) est le numéro des spécifications des EJB 3) ont été divisées en trois sous­spécifications :

l’API EJB 3 simplifiée (59 pages) ;

l’API Core Contracts and Requirements (562 pages) ;

la Java Persistence API (version 1.0, 256 pages).

Le modèle de programmation proposé par la version 2.1 des spécifications EJB, comportait une série de contraintes qui limitait l’usage de cette spécification et a entraîné l’apparition de solutions Open Source destinées à combler les carences que présentaient les EJB 2.1.

L’objectif des EJB 3 est de simplifier le développement d’applications Java et de standardiser l’API de persistance pour la plate­forme Java.

Une différence majeure apparaît dans la création des entity beans puisqu’ils sont maintenant basés sur la JPA.

Les spécifications des EJB 3 ont apporté une nouvelle API qui est plus simple pour implémenter les beans de sessions et y accéder. EJB 3 élimine l’obligation d’étendre :

les interfaces Home (javax.ejb.EJBHome, javax.ejb.EJBLocalHome),

les interfaces Remote component (javax.ejb.EJBObject, javax.ejb.EJBLocalObject),

l’interface javax.ejb.SessionBean dans le cas des session beans,

l’interface javax.ejb.EntityBean dans le cas des entity beans,

ainsi que d’avoir à écrire un fichier XML de déploiement très verbeux (ejb­jar.xml).

Les interfaces Remote component sont devenues des business interfaces. Les cycles de vie des beans sont les mêmes que dans la version 2.1 mais les méthodes callbacks sont plus simples.

Il n’est plus nécessaire de faire de lookup JNDI, l’injection de dépendance est utilisée à la place à l’aide des annotations.

La configuration par défaut simplifie également le développement puisqu’elle permet la mise en place de valeurs par défaut.

Le langage de requêtes EJB QL (Query Language) laisse la place au langage JPQL (JPA Query Language) qui donne enfin la possibilité au développeur d’écrire des requêtes avec des sous­requêtes, d’utiliser les clauses GROUP BY et HAVING, etc.

3. Avantages des EJB 3

Les avantages que représentent les EJB 3 par rapport aux versions précédentes et de manière générale sont importants. Ils sont bien évidemment examinés de plus près tout au long de l’ouvrage.

Le premier avantage est la simplicité du développement depuis la version 3, comme décrit dans le paragraphe précédent. Si bien que les EJB sont devenus une solution intéressante même pour des petites applications. De plus, grâce à cette simplification, de nouveaux collaborateurs peuvent être rapidement formés aux EJB pour monter en compétence et intégrer une équipe.

Un deuxième avantage est bien évidemment la multitude de services qu’offre le container du serveur d’application : service de nommage JNDI, scheduling avec les EJB Timers, gestion des transactions, concurrence, authentification, sécurité, pools de ressources, persistance... De plus les serveurs d’application peuvent facilement être mis en cluster pour faire face à des montées en charge.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mg7UTC5hH82ICwA=-enidentnumber

Page 6: Ejb Jsf Struts Flex Jasper

Un autre avantage est la flexibilité qu’apportent les EJB puisqu’ils contribuent à une meilleure répartition des rôles dans une équipe de développement ainsi qu’au respect des règles métiers.

De manière générale, les EJB apportent de la haute disponibilité et de la scalabilité grâce au load­balancing ainsi qu’au clustering.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mg7UTC5hH82ICwA=-enidentnumber

Page 7: Ejb Jsf Struts Flex Jasper

Présentation de l’application VenteEnligne

L’application développée dans cet ouvrage s’appelle VenteEnLigne. C’est, comme son nom l’indique, une application classique de commerce électronique proposant à la vente en ligne des produits provenant de catalogues variés (sport, informatique, bricolage, etc). Au niveau juridique, il est bon de savoir que la CNIL (Commission Nationale de l’Informatique et des Libertés) a mis à disposition sur son site un guide pratique qui explique les obligations des créateurs de sites marchands vis­à­vis de la CNIL.

1. Description fonctionnelle de l’application

Un client se connecte sur le site marchand pour visualiser les produits vendus et passer des commandes. Il peut mettre à jour son profil (nom, prénom, adresse etc.), consulter le détail d’un article, modifier les quantités des articles souhaités, consulter l’historique de ses commandes ainsi que donner son avis sur des produits en leur attribuant des notes. Pour pouvoir consulter le magasin en ligne et pour simplifier les choses, l’utilisateur doit obligatoirement être connecté. Une alternative possible et de plus en plus courante est de permettre à tous les utilisateurs, qu’ils soient connectés ou non connectés (anonymes), de consulter le magasin et d’ajouter des articles dans leurs paniers. Dans ce cas, ils devront se connecter ou s’enregistrer au moment du paiement.

Le paiement en ligne est effectué et la commande au format PDF est envoyée par courrier électronique au client.

Les fonctionnalités de l’application sont les suivantes :

une page de connexion et d’inscription ;

une page principale qui offre l’accès aux principales fonctionnalités ;

la présentation des catalogues, des produits et des articles disponibles en base (quantité, nom, prix…) ;

un profil ainsi qu’un panier, et la recherche de produits par mots­clefs ;

l’ajout d’un article dans le panier ;

la suppression d’un article du panier ;

le passage de commande avec la visualisation du contenu du panier ;

la connexion en tant que modérateur et administrateur, gestion des produits et/ou des clients.

2. Diagrammes de cas d’utilisation, de séquences et de classes UML

a. Les diagrammes de cas d’utilisation

Les diagrammes de cas d’utilisation (Use Cases en anglais) permettent de modéliser les processus et de décrire des cas concrets. Ils permettent de visualiser les acteurs, leur rôle et les actions qu’ils peuvent réaliser.

On peut identifier trois acteurs pour notre site de vente en ligne :

Les clients. Ils possèdent un profil, consultent les produits en ligne et passent des commandes.

Les gestionnaires. Ils mettent à jour les produits à vendre, en supprimant ou en ajoutant des produits à la vente. Ils peuvent modifier les prix de ces produits. Enfin, ils peuvent ajouter/supprimer des clients et gérer les détails des clients.

Les administrateurs. Ils peuvent également gérer des produits et des clients, mais aussi des modérateurs.

Voici trois diagrammes de scénarios possibles pour ces trois acteurs.

Diagramme de scénario : commander et payer

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 8: Ejb Jsf Struts Flex Jasper

Le stéréotype <<include>> représente une dépendance entre plusieurs cas d’utilisation : un cas d’utilisation inclut obligatoirement un autre cas d’utilisation.

Diagramme de scénario : gérer les produits

Diagramme de scénario : administrer le site

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 9: Ejb Jsf Struts Flex Jasper

Il existe de nombreux outils Open Source de modélisation UML, tel que StarUML, très facile d’utilisation et très léger, capable de faire du reverse engineering et de générer du code.

b. Les diagrammes de séquences

Grâce aux diagrammes de séquences, nous allons pouvoir modéliser de façon chronologique les différents échanges entre les objets.

Voici quelques exemples de diagrammes de séquences qui décrivent certains échanges de l’application :

Ajout d’un produit et paiement

Le diagramme suivant illustre le workflow possible d’un achat. Lorsqu’un client a créé son panier et effectué sa dernière sélection de produits, il peut vérifier le contenu de son panier afin d’y apporter quelques éventuelles modifications avant de passer commande et rentrer les détails pour le paiement.

Recherche d’un produit par critère

Le client entre un critère de recherche tel que le nom d’un livre ou le nom d’un auteur pour filtrer les produits. L’écran est rafraîchi pour afficher la liste des résultats.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 10: Ejb Jsf Struts Flex Jasper

Suppression d’un produit

Un client supprime un produit de son panier. Une fois supprimé, ce produit ne peut être récupéré. Le détail du panier est mis à jour et le montant total est recalculé quand un produit est supprimé.

c. Les diagrammes de classes

Cette section décrit les diagrammes de classes utilisés pour présenter les classes, les liens entre elles, ainsi que les interfaces de l’application.

Diagramme de classe du Client (Utilisateur)

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 11: Ejb Jsf Struts Flex Jasper

Diagramme de classe Utilisateur

Diagramme de classe du Produit

Au fur et à mesure du développement des classes de l’application, le modèle UML peut être amené à changer, et inversement, le code des classes peut changer si le modèle change aussi (reverse engineering).

Il y a donc une synchronisation qui s’effectue. C’est l’approche RoundTrip.

Elle est un peu à l’opposé de l’approche Model Driven, dans laquelle il faut suivre rigoureusement le modèle existant.

3. Architecture logicielle

Les phases de conception et de réalisation d’une application passent par la mise en place d’une architecture physique (dite également matérielle ou technique) et d’une architecture logicielle. L’architecture physique décrit les composants

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 12: Ejb Jsf Struts Flex Jasper

physiques (postes de développement, serveurs, bases de données, équipements réseaux…) utilisés pour le développement et pour le déploiement. L’architecture logicielle décrit les solutions mises en place (frameworks, logiciels, design patterns…) ainsi que leurs interactions.

Pour réaliser la partie dynamique du site de vente en ligne, nous allons suivre un développement en couches applicatives que propose l’architecture logicielle de type 3­tiers. Le modèle d’architecture logicielle en couches présente plusieurs avantages, notamment une meilleure répartition des rôles (chaque couche a un rôle précis), une séparation des traitements, une réduction du couplage (la dépendance) entre les services pour fournir une plus grande souplesse au niveau de la maintenabilité et de l’évolution.

Voici un schéma des trois couches de l’application :

Chaque couche ne communique qu’avec les couches voisines. Il existe également un autre modèle d’architecture logicielle qui préconise le développement en cinq couches :

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 13: Ejb Jsf Struts Flex Jasper

Dans ce modèle à cinq couches, la couche métier du modèle à trois couches se retrouve dans la couche service. Une couche domaine est ajoutée pour encapsuler les interactions avec la base. Elle peut correspondre à un DAO implémenté dans un session bean avec des méthodes génériques d’accès en base. Un client du DAO peut être un autre session bean, de la couche services, dans lequel ce DAO est injecté. Un exemple d’un tel DAO générique est décrit dans le chapitre Les session beans.

La couche application, également appelée coordination, gère l’organisation des données à afficher.

a. La couche présentation

Cette couche prend en charge les interactions utilisateur. Elle contient les composants graphiques de l’interface homme­machine pour l’affichage et la saisie, avec un premier niveau de contrôle (champ obligatoire par exemple). Elle expose donc les données à l’utilisateur, interprète ses commandes et gère l’enchaînement des vues. Elle est souvent gérée par un framework qui facilite la gestion des échanges avec l’utilisateur et/ou la gestion de l’interface graphique. Dans l’application, elle est développée dans un premier temps avec des pages JSP avec Struts 2, puis dans un deuxième temps avec JSF 1.2 / JSF 2 et enfin avec Flex 3. Les éventuelles modifications faites dans cette couche ne devraient pas en théorie entraîner des modifications dans la couche métier.

Les accès aux session beans sont facilités par la création d’une classe qui centralise ces accès, conformément au design pattern Service locator (cf. chapitre Struts 2 ­ Création d’un Service locator). De plus, les appels aux entity beans se font via les session beans afin de ne pas exposer les entity beans à la couche présentation. C’est l’un des buts du design pattern Session Façade (décliné du design pattern Façade) qui permet de masquer la complexité des interactions avec les objets de la couche de persistance.

b. La couche métier / business

Cette couche prend en charge la logique métier. Elle contient la logique des traitements (des entités pourvues de méthodes) et constitue donc le cœur de l’application. Ces traitements sont sollicités soit par la couche présentation soit par d’autres traitements. Les différentes règles de gestion et de contrôle de l’application sont implémentées dans cette couche. Elle offre des services applicatifs et métiers à la couche présentation. Elle est développée avec les EJB 3 (session beans, message­driven beans). Les règles de sécurité d’accès aux services métier sont également définies dans cette couche.

Il est préférable d’encapsuler les accès à la base dans un stateless session bean dédié, conformément au design pattern DAO (Direct Access Object, voir le chapitre Les session beans ­ Objets DAO utilisateur et client).

c. La couche persistance

Cette couche prend en charge l’accès aux données. Elle est chargée de faire le mapping entre les objets de la

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 14: Ejb Jsf Struts Flex Jasper

couche métier et les données en base. Pour effectuer des actions dans la base de données, les classes de la couche métier font appel aux classes de persistance qui sont basées sur les entity beans et la Java Persistence API (JPA) avec JPQL, le langage de requêtes objet.

L’ensemble est développé sous Java 6 et déployé sur le serveur d’application Open Source JBoss, l’un des premiers à implémenter les spécifications des EJB 3. On utilise la base de données Oracle 10g Express, l’annuaire LDAP ApacheDS et l’IDE Eclipse.

Le couplage désigne le degré de dépendance entre les couches. Un couplage lâche (loose coupling en anglais) est un principe général dans une architecture et qui préconise de réduire les dépendances entre les

couches.

4. Définition de la base

Il est maintenant temps de définir le schéma des tables de la base Oracle.

L’approche choisie est de d’abord créer les tables puis d’écrire les EJB entity beans, comme décrit dans le chapitre suivant. Cependant JPA permet aussi la démarche inverse, en générant le schéma de bases de données à partir des EJB entity beans.

a. Description des tables

Utilisateur (Id, Email, Fax, Login, Nom, Password, Prenom, Telephone, Titre, Type_Util, Adresse_FK).

Un utilisateur peut être un client, un administrateur ou un gestionnaire. Il peut avoir une ou plusieurs adresses.

Adresse (Id, CodePostal, Departement, Numero, Pays, Rue, Ville).

L’adresse de résidence et de facturation du client.

Produit (Id, Description, Nom, Catalogue_FK).

Un produit appartient à un et un seul catalogue.

Catalogue (Id, Description, Nom).

Un catalogue contient un ensemble de produits.

Stock (Id, Quantite).

La quantité disponible d’un article donné.

Article (Id, Nom, Prix, Produit_FK, Stock_FK).

Un article fait partie d’un produit et est disponible dans une certaine quantité.

Par exemple, les articles ballon et sifflet font partie du produit Football qui provient du catalogue Sport.

Les articles raquette et balle font partie du produit Tennis qui provient également du catalogue Sport.

Commande (Id, Date_Expiration_CarteCredit, Date_Commande, Numero_CarteCredit, Type_CarteCredit, Adresse_Fk, Utilisateur_FK).

La commande d’un client.

LigneCommande (Id, Quantite, Article_FK).

La ligne de commande concerne un seul article et indique la quantité commandée.

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 15: Ejb Jsf Struts Flex Jasper

LigneCommande_TJ (Commande_FK, LigneCommande_FK).

Table d’association qui fait le lien entre LigneCommande et Commande.

Voici le diagramme d’entités correspondant au schéma de la base de données :

Les clés claires et foncées correspondent respectivement aux clés primaires et aux clés étrangères.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp6nT0NhH82ICwA=-enidentnumber

Page 16: Ejb Jsf Struts Flex Jasper

Introduction

Les EJB 3 simplifient grandement le développement grâce à l’utilisation d’un modèle de programmation basé sur les POJOs (Plain Oriented Java Objects) et les annotations.

Les entity beans de type CMP (version 2.1 des EJB), c’est­à­dire les entity beans dont la persistance est managée par le container, sont remplacés par la Java Persistance API (JPA, JSR­220). Celle­ci peut fonctionner en dehors d’un container EJB, comme vous allez le voir dans ce chapitre. Elle est incluse dans les spécifications des EJB 3.

La plupart des différentes manipulations nécessaires à la création des projets et à leurs configurations, ainsi que la création des classes, sont décrites dans Eclipse, l’outil IDE utilisé dans cet ouvrage.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrZjnINhH82ICwA=-enidentnumber

Page 17: Ejb Jsf Struts Flex Jasper

Le mapping des classes et des tables

1. Le cycle de vie des objets persistants gérés par JPA

Un entity bean est un objet persistant qui est associé à un cycle de vie que le client contrôle et qu’il est donc important de reproduire ici pour en avoir une bonne compréhension :

On distingue quatre états principaux d’un entity bean par rapport au contexte de persistance :

Nouveau (new) : c’est un bean qui vient d’être instancié. Il n’est associé à aucun contexte de persistance.

Managé (managed) : le bean est associé à un contexte de persistance. Par exemple suite à une recherche, une sauvegarde ou une modification.

Détaché (detached) : le bean est détaché du contexte de persistance. Il n’est plus synchronisé avec la base.

Supprimé (removed) : le bean est attaché au contexte de persistance mais est prévu pour être supprimé de la base.

Les méthodes de l’interface javax.persistence.EntityManager (API JPA) conduisant à ces états sont les suivantes :

refresh() : dans la mesure où les entités peuvent utiliser un cache pour stocker les données, cette méthode permet de mettre à jour les entités par rapport à la base. L’entité devient managée.

merge() : cette méthode synchronise une entité détachée avec le contexte de persistance. L’entité détachée devient alors managée.

persist() : l’entité est insérée en base. Elle devient du coup managée.

remove() : l’entité est supprimée de la base après commit. Ce qui la conduit à l’état supprimé.

flush() : cette méthode synchronise de façon explicite le contexte de persistance avec la base.

2. La création du projet Enterprise Application

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 18: Ejb Jsf Struts Flex Jasper

Il faut commencer par créer un projet de type Enterprise Application. Ce projet est le projet central de l’application puisqu’il définit, via le fichier de configuration application.xml, le contenu de l’archive EAR qui sera produite à la compilation et utilisée pour le déploiement. Définissez d’abord le répertoire de développement dans Eclipse :

Cliquez sur File ­ Switch Workspace ­ Other et entrez le chemin du répertoire de travail (workspace), par exemple K:\ENI\DeveloppementEJB3\workspaceGalileo.

La suite consiste à créer un environnement de développement puis un projet EJB à l’intérieur de cet environnement. Dans la vue de l’explorateur de projet, cliquez sur le triangle renversé pour afficher le menu de cette vue et lancer la fenêtre de création de l’environnement de développement. Appelez­le EnvironnementEJB3 par exemple.

Le projet Enterprise Application est déployé sous la forme d’une archive EAR. Il est composé de modules qui sont mappés vers d’autres projets Java EE.

Dans la perspective Java EE, allez dans le menu d’Eclipse : File ­ New ­ Other … ­ Java EE ­ Enterprise Application Project.

Nommez­le VenteEnLigne, laissez les paramètres par défaut et assurez­vous que le serveur d’application est JBoss (cliquez sur New pour le créer s’il n’est pas déjà présent dans la combo box).

Au moment de l’écriture de ce chapitre, les adaptateurs de serveurs disponibles pour JBoss sont limités à la version 5. Cependant l’installation d’une version plus récente du serveur dans Eclipse reste possible avec

l’utilisation d’un adaptateur de serveur d’une précédente version. La version de JBoss qui apparaît dans certains écrans est la 5 alors que celle utilisée est en fait la 6. Reportez­vous au chapitre Mise en place de l’environnement pour l’installation détaillée de JBoss.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 19: Ejb Jsf Struts Flex Jasper

Cliquez sur Next. Dans l’écran suivant, cliquez sur New Module et sélectionnez les quatre modules Java EE qui sont proposés : VenteEnLigneClient, VenteEnLigneEJB, VenteEnLigneWeb et VenteEnLigneConnector.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 20: Ejb Jsf Struts Flex Jasper

Cliquez sur Finish pour revenir à l’écran initial.

Enfin cochez la case Generate application deployment descriptor et cliquez sur Finish. Au final, quatre projets ont été créés. Ils seront packagés dans une archive EAR dont le contenu correspond au suivant, en conformité avec les règles Java EE :

META­INF/application.xml

archive WAR (Web Application aRchive)

archive JAR (Java Application aRchive)

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 21: Ejb Jsf Struts Flex Jasper

3. Le projet VenteEnLigneEJB

Le projet VenteEnLigneEJB est destiné à contenir les EJB session beans, message­driven beans et les EJB Timers. Il contiendra également les entity beans et les interceptors.

a. Ajout des librairies requises au projet VenteEnLigneEJB

Pour pouvoir accéder à la base, il est nécessaire d’ajouter le driver JDBC Oracle dans le classpath du projet : ojdbc14.jar. Le driver implémente le protocole de transfert des requêtes et de leurs résultats entre le client et la base, via l’API JDBC (Java DataBase Connectivity).

Celui­ci est disponible en téléchargement sur le site d’Oracle à l’adresse suivante :

http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/htdocs/jdbc_10201.html

Copiez cette librairie dans le répertoire server\default\lib de JBoss. Allez ensuite dans les propriétés du projet en faisant un clic droit sur le projet et en choisissant le menu Properties, puis cliquez dans Java Build Path pour sélectionner l’onglet Libraries.

Ajoutez le driver en cliquant sur le bouton Add JARs et sélectionnez ojdbc14.jar. Cliquez sur OK.

b. Création d’une datasource

Après avoir créé une base de données, il faut maintenant la rendre disponible dans Eclipse. Ceci s’effectue en deux étapes :

1 ­ Création de la définition du driver Oracle JDBC (ceci revient à faire pointer Eclipse vers la librairie Jar du driver JDBC).

2 ­ Création de la connexion Oracle JDBC.

Les captures d’écran et commentaires qui suivent vous aideront dans ce processus.

Cliquez sur Windows ­ Open Perspective ­ Other ­ Database Development.

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 22: Ejb Jsf Struts Flex Jasper

Faites un clic droit sur le nœud Databases et sélectionnez New.

Choisissez Oracle comme type de base de données et nommez­la BaseOracleVenteEnLigne.

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 23: Ejb Jsf Struts Flex Jasper

Cliquez sur Next. Il faut maintenant spécifier un driver. Créez­le s’il n’existe pas encore. Pour cela, cliquez sur le bouton New Driver Definition (à droite de la combobox de la liste des drivers).

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 24: Ejb Jsf Struts Flex Jasper

Sélectionnez ensuite l’onglet Jar list et cliquez sur le bouton Edit Jar/Zip pour ajouter le driver ojdbc14.jar qui se trouve dans le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\lib. Cliquez sur OK, spécifiez les propriétés de connexion JDBC (SID, Host, User name, Password) et testez la connection. Enfin cliquez sur Finish. La datasource Oracle est maintenant configurée dans Eclipse et prête à être utilisée dans les projets.

c. Ajout de la persistance JPA au projet EJB

Dans JBoss, il existe un lien fort entre le framework de persistance Hibernate et les EJB 3. En effet, initialement Hibernate est apparu, puis une spécification JPA sur la persistance est née, fortement inspirée d’Hibernate. Hibernate est devenu par la suite une implémentation de JPA. Les spécifications EJB 3 requièrent le support d’un modèle de persistance qui doit reposer sur l’API JPA (package javax.persistence). Un serveur d’application qui implémente les EJB 3 dans son container doit donc supporter JPA. JBoss EJB 3 a fait le choix d’utiliser l’implémentation d’Hibernate de JPA.

Il faut aussi savoir qu’Hibernate est composé de trois principaux modules :

Hibernate core (avec les fichiers de mapping hbm.xml) ;

Hibernate avec les annotations (l’annotation @Entity du package org.hibernate.annotations par exemple) ;

Hibernate EntityManager en tant que provider JPA.

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 25: Ejb Jsf Struts Flex Jasper

De plus, Gavin King et Emmanuel Bernard, deux des principaux développeurs d’Hibernate, font partie du groupe d’experts en charge de la spécification JPA (JSR­220) et qui fait partie des EJB 3.

Eclipse facilite le développement des entity beans grâce à WTP (Web Tools Platform) qui est inclut dans la distribution. En effet cette plate­forme inclut des outils pour créer un projet JPA et faire du reverse engineering pour créer automatiquement les entity beans.

Cela correspond à l’approche Bottom­Up qui consiste à d’abord créer le schéma de la base de données puis à développer les entity beans.

Le projet Eclipse qui supporte JPA s’appelle Dali. Il tire son nom du peintre espagnol Salvator Dali en référence à son tableau « La persistance de la mémoire ». Le projet Dali est en fait un sous­projet de WTP

qui contient de nombreux outils pour faciliter le développement. Le site officiel est http://www.eclipse.org/webtools/dali/. La démarche entreprise ici est donc de d’abord créer le schéma des tables en base Oracle de façon manuelle, puis de générer les entités en utilisant l’outil Dali.

Maintenant que vous avez connecté Eclipse à la base Oracle, vous pouvez continuer et créer le projet EJB / JPA. Ce processus se fait également en deux étapes :

1 ­ Création de la définition du serveur d’application (ceci revient à faire pointer Eclipse vers l’installation de JBoss) si elle n’existe pas encore.

2 ­ Ajout de la persistance JPA au projet EJB.

Faites un clic droit sur le projet VenteEnLigneEJB et allez dans les propriétés du projet. Puis allez sur Project Facets et cochez la case Java Persistence.

Le projet EJB peut maintenant utiliser Dali pour générer les entities.

d. Génération des entities à partir des tables

Vous allez maintenant générer les entity beans à partir de la base, conformément à l’approche dite bottom­up. Pour cela faites un clic droit sur le projet EJB, cliquez sur Properties puis JPA. Décochez la case Annotated classes must be listed in persistence.xml et cochez Discover annotated classes automatically.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 26: Ejb Jsf Struts Flex Jasper

Cliquez sur OK. Faites de nouveau un clic droit sur le projet EJB, cliquez sur JPA Tools, et Generate Entities. Choisissez la connexion et le schéma appropriés.

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 27: Ejb Jsf Struts Flex Jasper

Cliquez sur Next et nommez le package com.eni.dvtejb.metier.entities, décochez la case à cocher Synchronize Classes in persistence.xml, sélectionnez toutes les tables et cliquez sur Finish.

Les entity beans ont été automatiquement générés.

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 28: Ejb Jsf Struts Flex Jasper

Comme le montre le code généré, pour spécifier qu’une classe est une entité, il faut l’annoter avec l’annotation @Entity. Elle fait partie du package javax.persistence de l’API JPA. Ce package est constitué principalement d’annotations.

Voici le code de l’entity bean Adresse :

Entité Adresse :

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.math.BigDecimal; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; @Entity @SequenceGenerator(name="SeqAdresse", sequenceName="adresse_seq") public class Adresse implements Serializable @Id @ GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SeqAdresse") private long adresseid; private String rue; private BigDecimal codepostal; private String ville; private String departement; private String pays; @OneToMany(mappedBy="adresseFk") private Set<Client> clientCollection; @OneToMany(mappedBy="adresseFk") private Set<Commande> commandeCollection;

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 29: Ejb Jsf Struts Flex Jasper

private static final long serialVersionUID = 1L; public Adresse() super(); public long getAdresseid() return this.adresseid; public void setAdresseid(long adresseid) this.adresseid = adresseid; public String getRue() return this.rue; public void setRue(String rue) this.rue = rue; public BigDecimal getCodepostal() return this.codepostal; public void setCodepostal(BigDecimal codepostal) this.codepostal = codepostal; public String getVille() return this.ville; public void setVille(String ville) this.ville = ville; public String getDepartement() return this.departement; public void setDepartement(String departement) this.departement = departement; public String getPays() return this.pays; public void setPays(String pays) this.pays = pays; public Set<Client> getClientCollection() return this.clientCollection; public void setClientCollection(Set<Client> clientCollection) this.clientCollection = clientCollection; public Set<Commande> getCommandeCollection() return this.commandeCollection; public void setCommandeCollection(Set<Commande> commandeCollection) this.commandeCollection = commandeCollection;

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 30: Ejb Jsf Struts Flex Jasper

Par défaut, la table associée à un bean entity porte le même nom que la classe. Tous les entity beans doivent avoir un constructeur public ou protected sans arguments. Les types valides des champs persistants sont les suivants :

types primitives ;

des classes sérializables ;

java.util.Collection ou java.util.Set.

L’entité Catalogue contient une relation OneToMany avec l’entité Produit. En effet, un catalogue contient plusieurs produits et un produit appartient à un et un seul catalogue.

Entité Catalogue :

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; @Entity public class Catalogue implements Serializable @Id private long catalogueid; private String description; private String nom; @OneToMany(mappedBy="catalogueFk") private Set<Produit> produitCollection; private static final long serialVersionUID = 1L; public Catalogue() super(); public long getCatalogueid() return this.catalogueid; public void setCatalogueid(long catalogueid) this.catalogueid = catalogueid; public String getDescription() return this.description; public void setDescription(String description) this.description = description; public String getNom() return this.nom; public void setNom(String nom) this.nom = nom; public Set<Produit> getProduitCollection() return this.produitCollection;

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 31: Ejb Jsf Struts Flex Jasper

public void setProduitCollection(Set<Produit> produitCollection) this.produitCollection = produitCollection;

L’examen des classes générées montre que les relations entre les classes ont été préservées. C’est notamment le cas de l’entité Produit qui a une relation bidirectionnelle avec l’entité Catalogue.

Entité Produit :

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @Entity public class Produit implements Serializable @Id private long produitid; private String description; private String nom; @ManyToOne @JoinColumn(name="CATALOGUE_FK") private Catalogue catalogueFk; @OneToMany(mappedBy="produitFk") private Set<Article> articleCollection; private static final long serialVersionUID = 1L; public Produit() super(); public long getProduitid() return this.produitid; public void setProduiti(long produitid) this.produitid = produitid; public String getDescription() return this.description; public void setDescription(String description) this.description = description; public String getNom() return this.nom; public void setNom(String nom) this.nom = nom; public Catalogue getCatalogueFk()

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 32: Ejb Jsf Struts Flex Jasper

return this.catalogueFk; public void setCatalogueFk(Catalogue catalogueFk) this.catalogueFk = catalogueFk; public Set<Article> getArticleCollection() return this.articleCollection; public void setArticleCollection(Set<Article> articleCollection) this.articleCollection = articleCollection;

Le mapping des classes avec la base de données fait usage des annotations définies dans le package javax.persistence. Les annotations peuvent être définies soit au niveau des champs, soit au niveau des getters. Il est cependant possible de faire un mapping via des fichiers XML. Dans ce cas ces fichiers doivent être déclarés soit dans un fichier orm.xml placé dans le répertoire META_INF, soit dans un fichier de configuration persistence.xml.

La table d’association LIGNECOMMANDE_TJ est traduite en relation ManyToMany entre l’entité Commande et l’entité LigneCommande. Elle est indiquée à l’aide de l’annotation @JoinTable.

Il existe d’autres annotations et attributs d’annotations qu’il est utile de connaître et que vous pouvez ajouter aux classes générées. Ainsi l’annotation @ManyToMany possède plusieurs attributs tel que cascade, fetch et mappedBy. Ces attributs seront détaillés par la suite.

L’annotation @Table peut être ajoutée au niveau du nom de l’entité pour spécifier la table à mapper. Par défaut, le nom est celui de l’entité. On peut également y spécifier le schéma (Oracle) ou le catalog (MySql).

L’entité Commande représente une commande d’un certain nombre d’articles. L’entité Lignecommande représente la commande pour un seul article. En terme d’objets, une commande contient donc une liste de lignes de commandes et chaque ligne de commande contient une référence à un article :

Voici les entités Commande et Lignecommande générées par le plugin Dali.

Entité Commande :

package com.eni.dvtejb.metier.entities ; import java.io.Serializable ; import java.util.Date ; import java.util.Set ; import javax.persistence.Column ; import javax.persistence.Entity ; import javax.persistence.Id ; import javax.persistence.JoinColumn ; import javax.persistence.JoinTable ; import javax.persistence.ManyToMany ; import javax.persistence.ManyToOne ; @Entity public class Commande implements Serializable @Id private long commandeid ; private Date datecommande ; @Column(name="TYPE_CARTECREDIT") private String typeCartecredit; @Column(name="NUMERO_CARTECREDIT") private String numeroCartecredit;

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 33: Ejb Jsf Struts Flex Jasper

@Column(name="DATE_EXPIRATION_CARTECREDIT") private Date dateExpirationCartecredit ; @ManyToOne @JoinColumn(name= "ADRESSE_FK") private Adresse adresseFk ; @ManyToOne @JoinColumn(name= "CLIENT_FK") private Client clientFk ; @ManyToMany

@JoinTable(name= "LIGNECOMMANDE_TJ",

joinColumns=@JoinColumn(name= "COMMANDE_FK"),

inverseJoinColumns=@JoinColumn(name= "LIGNECOMMANDE_FK"))

private Set<Lignecommande> lignecommandeCollection;

private static final long serialVersionUID = 1L; public Commande() super(); public long getCommandeid() return this.commandeid; public void setCommandeid(long commandeid) this.commandeid = commandeid; public Date getDatecommande() return this.datecommande; public void setDatecommande(Date datecommande) this.datecommande = datecommande; public String getTypeCartecredit() return this.typeCartecredit; public void setTypeCartecredit(String typeCartecredit) this.typeCartecredit = typeCartecredit; public String getNumeroCartecredit() return this.numeroCartecredit; public void setNumeroCartecredit(String numeroCartecredit) this.numeroCartecredit = numeroCartecredit; public Date getDateExpirationCartecredit() return this.dateExpirationCartecredit; public void setDateExpirationCartecredit(Date dateExpirationCartecredit) this.dateExpirationCartecredit = dateExpirationCartecredit; public Adresse getAdresseFk() return this.adresseFk;

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 34: Ejb Jsf Struts Flex Jasper

public void setAdresseFk(Adresse adresseFk) this.adresseFk = adresseFk; public Client getClientFk() return this.clientFk; public void setClientFk(Client clientFk) this.clientFk = clientFk; public Set<Lignecommande> getLignecommandeCollection() return this.lignecommandeCollection; public void setLignecommandeCollection(Set<Lignecommande> lignecommandeCollection) this.lignecommandeCollection = lignecommandeCollection ;

Entité LigneCommande :

package com.eni.dvtejb.metier.entities ; import java.io.Serializable ; import java.util.Set ; import javax.persistence.Entity ; import javax.persistence.Id ; import javax.persistence.JoinColumn ; import javax.persistence.ManyToMany ; import javax.persistence.ManyToOne ; @Entity public class Lignecommande implements Serializable @Id private long lignecommandeid ; private int quantite ; @ManyToOne @JoinColumn(name= "ARTICLE_FK") private Article articleFk ; @ManyToMany(mappedBy="lignecommandeCollection")

private Set<Commande> commandeCollection;

private static final long serialVersionUID = 1L; public Lignecommande() super(); public long getLignecommandeid() return this.lignecommandeid; public void setLignecommandeid(long lignecommandeid) this.lignecommandeid = lignecommandeid; public int getQuantite() return this.quantite; public void setQuantite(int quantite)

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 35: Ejb Jsf Struts Flex Jasper

this.quantite = quantite ; public Article getArticleFk() return this.articleFk; public void setArticleFk(Article articleFk) this.articleFk = articleFk; public Set<Commande> getCommandeCollection() return this.commandeCollection; public void setCommandeCollection(Set<Commande> commandeCollection) this.commandeCollection = commandeCollection ;

L’annotation @Id indique que l’attribut lignecommandeid correspond à la clé primaire de la table. Chaque entité DOIT posséder une clé primaire. L’annotation peut être écrite sur l’attribut ou sur une des propriétés de l’attribut (getter / setter). Il est possible de spécifier la stratégie de génération de la clé primaire via l’annotation @GeneratedValue. Si la valeur est AUTO, cela signifie qu’on laisse le fournisseur JPA choisir la stratégie.

Les stratégies de génération de la clé primaire sont définies à l’aide d’une énumération GenerationType :

public enum GenerationType TABLE, SEQUENCE, IDENTITY, AUTO ;

La stratégie SEQUENCE est la stratégie choisie dans l’application :

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SeqLigneCommande") private long lignecommandeid;

La stratégie choisie doit prendre en compte le type de bases de données. Ainsi la stratégie IDENTITY est supportée par MySQL, DB2, SQL Server mais pas par Oracle qui peut cependant simuler des colonnes IDENTITY avec des séquences.

Les séquences des tables CATALOGUE, UTILISATEUR, ADRESSE, COMMANDE, LIGNECOMMANDE, ARTICLE sont créées de façon manuelle en base.

create sequence catalogue_seq minvalue 15 start with 15 increment by 1 create sequence utilisateur_seq minvalue 15 start with 15 increment by 1 create sequence adresse_seq minvalue 15 start with 15 increment by 1 create sequence commande_seq minvalue 15 start with 15 increment by 1 create sequence lignecommande_seq minvalue 15

- 19 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 36: Ejb Jsf Struts Flex Jasper

start with 15 increment by 1 create sequence article_seq minvalue 0 start with 25 increment by 1

Ces séquences sont incrémentées de un à chaque fois. Pour indiquer à un entity bean qu’il doit utiliser la séquence, utilisez l’annotation @SequenceGenerator au niveau de la classe. L’attribut sequenceName a pour valeur le nom de la séquence en base.

L’entité Client possède une relation ManyToOne avec l’entité Adresse et une relation OneToMany avec l’entité Commande.

Entité Utilisateur

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.math.BigDecimal; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @Entity @SequenceGenerator(name="SeqUtilisateur", sequenceName="utilisateur_seq") public class Utilisateur implements Serializable @Id @ GeneratedValue(strategy = GenerationType.SEQUENCE, generator=" SeqUtilisateur ") private long utilisateurid; private String email; private BigDecimal fax; private String login; private String nom; private String password; private String prenom; private BigDecimal telephone; private String titre; @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name="ADRESSE_FK") private Adresse adresseFk; @OneToMany(mappedBy="clientFk") private Set<Commande> commandeCollection; private static final long serialVersionUID = 1L; public Client() super(); public long getUtilisateurid() return this.utilisateurid; public void setUtilisateurid(long utilisateurid) this. utilisateurid = utilisateurid; public String getEmail()

- 20 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 37: Ejb Jsf Struts Flex Jasper

return this.email; public void setEmail(String email) this.email = email; public BigDecimal getFax() return this.fax; public void setFax(BigDecimal fax) this.fax = fax; public String getLogin() return this.login; public void setLogin(String login) this.login = login; public String getNom() return this.nom; public void setNom(String nom) this.nom = nom; public String getPassword() return this.password; public void setPassword(String password) this.password = password; public String getPrenom() return this.prenom; public void setPrenom(String prenom) this.prenom = prenom; public BigDecimal getTelephone() return this.telephone; public void setTelephone(BigDecimal telephone) this.telephone = telephone; public String getTitre() return this.titre; public void setTitre(String titre) this.titre = titre; public Adresse getAdresseFk() return this.adresseFk; public void setAdresseFk(Adresse adresseFk) this.adresseFk = adresseFk;

- 21 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 38: Ejb Jsf Struts Flex Jasper

public Set<Commande> getCommandeCollection() return this.commandeCollection; public void setCommandeCollection(Set<Commande> commandeCollection) this.commandeCollection = commandeCollection;

Un produit est composé de plusieurs articles et un article peut faire partie de plusieurs commandes.

Entité Article

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @Entity @SequenceGenerator(name="SeqArticle", sequenceName="article_seq") public class Article implements Serializable @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SeqArticle") private long articleid; private String nom; private double prix; @ManyToOne @JoinColumn(name="PRODUIT_FK") private Produit produitFk; @OneToMany(mappedBy="articleFk") private Set<Lignecommande> lignecommandeCollection; private static final long serialVersionUID = 1L; public Article() super(); public long getArticleid() return this.articleid; public void setArticleid(long articleid) this.articleid = articleid; public String getNom() return this.nom; public void setNom(String nom) this.nom = nom;

- 22 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 39: Ejb Jsf Struts Flex Jasper

public double getPrix() return this.prix; public void setPrix(double prix) this.prix = prix; public Produit getProduitFk() return this.produitFk; public void setProduitFk(Produit produitFk) this.produitFk = produitFk; public Set<Lignecommande> getLignecommandeCollection() return this.lignecommandeCollection; public void setLignecommandeCollection(Set<Lignecommande> lignecommandeCollection) this.lignecommandeCollection = lignecommandeCollection;

e. Définition d’une source de données dans JBoss

L’application va se connecter à la base Oracle en utilisant JBoss. Il est alors nécessaire de spécifier un nom JNDI pour la source de données (data source).

1 ­ Cela consiste d’abord à copier la librairie ojdbc14.jar du driver JDBC dans le répertoire lib de JBoss : K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\server\default\lib.

2 ­ Puis à créer un fichier de configuration oracle­ds.xml de la base Oracle dans le répertoire deploy.

Un exemple d’un tel fichier se trouve dans le répertoire K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\docs\examples\jca. Ce fichier mappe une connexion au nom JNDI OracleDS. Il sera déployé et enregistré dans le service de nommage JNDI. La base Oracle remplace ainsi la base Hypersonic SQL qui est la base intégrée dans JBoss.

Voici un exemple de configuration possible du fichier deploy/oracle­ds.xml :

<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>OracleDS</jndi-name>

<connection-url>jdbc:oracle:thin:@localhost:1521:XE</connection-

url> <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

<user-name>hr</user-name>

<password>toto</password>

<exception-sorter-class- name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exce ption-sorter-class-name> <metadata> <type-mapping>Oracle11</type-mapping>

</metadata> </local-tx-datasource> </datasources>

f. Le fichier persistence.xml

- 23 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 40: Ejb Jsf Struts Flex Jasper

L’API de persistance JPA requiert la présence d’un fichier XML nommé persistence.xml, à l’intérieur du répertoire META­INF. Ce fichier XML inclut une liste de classes qui seront managées par l’API de persistance. Créez ce fichier persistence.xml dans le répertoire META­INF à l’intérieur du projet VenteEnligneEJB. Ce fichier définit une ou plusieurs unités de persistance et fait le lien entre les entity beans (POJOs) et la connexion à la base de données. Il est utilisé par la classe javax.persistence.Persistence pour créer l’EntityManagerFactory.

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.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_1_0.xsd"> <persistence-unit name="VenteEnLigneClient" transaction- type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>com.eni.dvtejb.metier.entities.Client</class> <class>com.eni.dvtejb.metier.entities.Adresse</class> <class>com.eni.dvtejb.metier.entities.Commande</class> <class>com.eni.dvtejb.metier.entities.Lignecommande</class> <class>com.eni.dvtejb.metier.entities.Article</class> <class>com.eni.dvtejb.metier.entities.Produit</class> <class>com.eni.dvtejb.metier.entities.Catalogue</class> <properties> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" /> <property name="hibernate.connection.username" value="hr" /> <property name="hibernate.connection.password" value="toto" /> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521/XE" /> <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties> /persistence-unit> </persistence>

Dans ce fichier, une première unité de persistance appelée VenteEnLigne est définie.

Une unité de persistance définit un ensemble de classes (entities) d’une application qui sont mappées à une base de données unique. Plusieurs unités de persistance peuvent exister dans le même fichier de persistance. Ensuite il faut définir le type de transaction qui peut être JTA (les transactions sont gérées par la Java Transaction API) ou RESOURCE_LOCAL (les transactions sont gérées par l’application). Enfin il faut spécifier les paramètres de connexion à la base et Hibernate en tant que provider JPA.

Notez la différence d’orthographe : persistance (avec un e) en anglais et persistance (avec un a) en français.

Il est possible de procéder de manière inverse en créant d’abord les entity beans, puis en créant les tables de façon automatique, grâce à Hibernate qui est une implémentation de JPA.

La propriété hibernate.hbm2ddl.auto signifie qu’Hibernate va automatiquement créer les tables et séquences de la base de données pendant le redéploiement. La valeur create­drop signifie que les tables et séquences seront supprimées et recrées à la volée. Par conséquent, toutes les données de la base seront perdues à chaque fois que l’application est redéployée.

La valeur update signifie qu’Hibernate créera automatiquement les tables si elles n’existent pas.

Pour pouvoir vérifier que les données sont bien insérées en base, il faut s’assurer que la valeur de la propriété hibernate.hbm2ddl.auto est à create et non create­drop afin de ne pas supprimer les tables

en fin de test : <property name="hibernate.hbm2ddl.auto" value="update" />.

- 24 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 41: Ejb Jsf Struts Flex Jasper

4. Création d’un client hors container avec JUnit

Les EJB 3.0 facilitent les tests grâce au modèle POJO. Il suffit d’invoquer les services de persistance à travers une classe de service ou bien directement à l’intérieur du client. Pour tester les entity beans, vous pouvez créer un client en utilisant le framework Junit. Ce framework Open Source, très utilisé pour les tests unitaires en langage Java, permet de vérifier si le comportement de chaque méthode de chaque classe Java est bien celui désiré. Ce client fonctionne hors container, c’est­à­dire que le serveur n’a pas besoin d’être démarré.

Dans le projet VenteEnLigneClient, créez le package com.eni.dvtejb.tests, faites un clic droit, New ­ Other ­ Java ­ JUnit ­ Junit Test Case.

Cliquez sur Next, cochez New Junit 4 Test, entrez le nom de classe PersistenceHorsContainerTest, cochez setUp() et tearDown().

- 25 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 42: Ejb Jsf Struts Flex Jasper

Terminez la création de cette classe de test en cliquant sur Finish puis OK sur l’écran suivant pour ajouter Junit 4 dans le build path. La classe PersistenceHorsContainerTest étend la classe TestCase du framework JUnit :

package com.eni.dvtejb.client; import java.math.BigDecimal; import java.util.Date; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import junit.framework.TestCase; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.entities.Commande; public class PersistenceHorsContainerTest extends TestCase private static Logger logger = Logger.getLogger(PersistenceHorsContainerTest.class.getName()); private EntityManagerFactory emFactory; private EntityManager em; public PersistenceHorsContainerTest(String testName)

- 26 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 43: Ejb Jsf Struts Flex Jasper

super(testName); @Override protected void setUp() throws Exception super.setUp(); try logger.info("JPA EntityManager pour tests unitaires "); emFactory = Persistence.createEntityManagerFactory("VenteEnLigneClientJavaSE"); em = emFactory.createEntityManager(); catch (Exception ex) ex.printStackTrace(); fail("Exception pendant l’instantiation du JPA EntityManager."); @Override protected void tearDown() throws Exception super.tearDown(); logger.info("Fermeture de la couche Hibernate JPA."); if (em != null) em.close(); if (emFactory != null) emFactory.close(); public void testPersistence() try logger.info("debut de testPersistence()"); em.getTransaction().begin(); logger.info("em.getTransaction().begin()"); Utilisateur client = new Utilisateur (); client.setNom("renard"); client.setPrenom("lulu"); BigDecimal fax = new BigDecimal("1115333"); client.setFax(fax); client.setLogin("renard"); client.setPassword("lulu"); BigDecimal telephone = new BigDecimal("1115333"); client.setTelephone(telephone); client.setTitre("Mr"); client.setEmail("[email protected]"); logger.info("avant persist"); em.persist(client); logger.info("apres persist"); assertTrue(em.contains(client)); Commande commande = new Commande(); commande.setTypeCartecredit("Visa"); Date aujourdhui = new Date(); long t = aujourdhui.getTime(); java.sql.Date aujourdhuiSQL = new java.sql.Date(t); commande.setDatecommande(aujourdhuiSQL); // Clé étrangère commande.setClientFk(client); java.sql.Date expirationDate = java.sql.Date.valueOf( "2010-01-31" ); commande.setDateExpirationCartecredit( expirationDate); commande.setNumeroCartecredit("4123654787651234");

- 27 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 44: Ejb Jsf Struts Flex Jasper

em.persist(commande); assertTrue(em.contains(commande)); Adresse adresse = new Adresse(); BigDecimal codepostal = new BigDecimal("75000"); adresse.setCodepostal(codepostal); adresse.setDepartement("Paris"); adresse.setVille("Paris"); adresse.setPays("France"); adresse.setRue("Vaugirard"); BigDecimal numero = new BigDecimal("230"); adresse.setNumero(numero); em.persist(adresse); em.remove(adresse); assertFalse(em.contains(adresse)); em.getTransaction().commit(); logger.info("fin de testPersistence()"); catch (Exception ex) em.getTransaction().rollback(); ex.printStackTrace(); fail("Exception pendant le test testPersistence");

Il est nécessaire d’ajouter le fichier de persistance persistence.xml dans le répertoire META­INF du projet VenteEnLigneClient. C’est lui qui va faire le lien entre la base de données et les entity beans.

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.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_1_0.xsd"> <persistence-unit name="VenteEnLigneClientJavaSE" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>com.eni.dvtejb.metier.entities.Utilisateur</class> <class>com.eni.dvtejb.metier.entities.Adresse</class> <class>com.eni.dvtejb.metier.entities.Commande</class> <class>com.eni.dvtejb.metier.entities.Lignecommande</class> <class>com.eni.dvtejb.metier.entities.Stock</class> <class>com.eni.dvtejb.metier.entities.Article</class> <class>com.eni.dvtejb.metier.entities.Produit</class> <class>com.eni.dvtejb.metier.entities.Catalogue</class> <properties> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" /> <property name="hibernate.connection.username" value="hr" /> <property name="hibernate.connection.password" value="toto" /> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521/XE" /> <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>

- 28 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 45: Ejb Jsf Struts Flex Jasper

Le fichier persistence.xml peut contenir plusieurs unités de persistance. Dans cet exemple, la classe cliente utilise l’unité de persistance VenteEnLigneClientJavaSE.

L’élément <class> spécifie les classes persistantes managées. Le type de transaction choisi est RESOURCE_LOCAL. Comme spécifié dans l’API JPA 1.0, dans un environnement hors container, c’est­à­dire Java SE, c’est la valeur par défaut du type de transaction. En revanche, dans un environnement Java EE, donc avec un serveur d’application, la valeur par défaut du type de transaction est JTA.

Le type de transaction RESOURCE_LOCAL signifie que les transactions sont gérées de façon explicite par l’application.

Il faut également récupérer les librairies cglib (http://cglib.sourceforge.net/) et asm (http://forge.objectweb.org/projects/asm). Installez­les dans les librairies du projet VenteEnLigneClient (Java Build Path).

Dans cet exemple, les entités Client et Commande sont ici persistées en base. Exécutez ce test en faisant un clic droit sur la classe PersistenceHorsContainerTest.java ­ Run as ­ Junit Test et vérifiez que les données sont bien présentes dans les tables UTILISATEUR et COMMANDE.

Les entity beans développés fonctionnent parfaitement en dehors d’un container EJB. Il n’a pas été nécessaire de déployer le projet sur le serveur. Il suffit de créer des POJO (Plain Oriented Java Objects), de spécifier comment les mapper avec les tables et les colonnes et d’utiliser un objet manager pour invoquer les services de persistance.

Conclusion

- 29 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mjcpn5BhH82ICwA=-enidentnumber

Page 46: Ejb Jsf Struts Flex Jasper

L’interface EntityManager

La persistance des entity beans est assurée par l’interface javax.persistence.EntityManager qui fournit des méthodes pour :

Créer, lire, mettre à jour et supprimer des entités (CRUD).

Sélectionner des entités à travers EJB QL ou des requêtes SQL (Structured Query Language).

Synchroniser les états des entités avec la base de données.

Acquérir des verrous sur les entités.

Une instance d’EntityManager est associée à un contexte de persistance. Un contexte de persistance est un ensemble d’instances d’entités dans lequel il existe une instance unique d’entité pour chacune de ces entités persistantes.

L’interface javax.persistence.EntityManager :

package javax.persistence; public interface EntityManager public void persist(Object entity); public <T> T find(Class <T> entityClass, Object primaryKey); public <T> T getReference(Class <T> entityClass, Object primaryKey); public <T> T merge(T entity); public void remove(Object entity); public void lock(Object entity, LockModeType lockMode); public void refresh(Object entity); public boolean contains(Object entity); public void clear( ); public void joinTransaction( ); public void flush( ); public FlushModeType getFlushMode( ); public void setFlushMode(FlushModeType type); public Query createQuery(String queryString); public Query createNamedQuery(String name); public Query createNativeQuery(String sqlString); public Query createNativeQuery(String sqlString, String resultSetMapping); public Query createNativeQuery(String sqlString, Class resultClass); public Object getDelegate( ); public void close( ); public boolean isOpen( );

Un EntityManager peut s’obtenir de trois façons :

1 ­ Dans une application Java SE, une factory EntityManagerFactory est utilisée pour créer un EntityManager.

private EntityManager emf = Persistence.createEntityManagerFactory("VenteEnLigneClient") ; private EntityManager em = emf.createEntityManager() ;

2 ­ Dans un environnement JAVA EE, un EntityManager peut être injecté dans un bean à l’aide de l’annotation @PersistenceContext.

PersistenceContext("VenteEnLigneClient") private EntityManager em ;

3 ­ Dans Java SE ou JAVA EE, via un lookup JNDI :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnZsT89hH82ICwA=-enidentnumber

Page 47: Ejb Jsf Struts Flex Jasper

@Resource SessionContext ctx ; EntityManager em = (EntityManager)ctx.lookup("VenteEnLigneClient") ;

La méthode persist() permet de sauvegarder l’entité en base de données. Il ne faut l’appeler que pour les entités nouvellement créées.

Un objet qui n’est pas créé par le container est non managé. Pour le rendre managé, il faut appeler la méthode merge() pour « merger » cet objet non managé au contexte de persistance.

La méthode find() permet de faire des recherches sur les entités. Dans l’application, elle est utile pour rechercher un client ou un article.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnZsT89hH82ICwA=-enidentnumber

Page 48: Ejb Jsf Struts Flex Jasper

La gestion des transactions

Le principal but des transactions est de s’assurer que toutes les opérations sont sauvées, ou qu’aucune opération n’est sauvée en base. Les transactions doivent respecter les propriétés référencées via l’acronyme ACID (Atomicité, Cohérence, Isolation, Durabilité) :

Atomicité : toutes les opérations d’une transaction doivent se dérouler avec succès. Sinon la transaction échoue.

Cohérence : lors d’une transaction, seules des données valides sont insérées, créées, modifiées ou supprimées en base. Par exemple, supprimer un utilisateur en base implique de supprimer également son adresse.

Isolation : plusieurs transactions qui se déroulent en même temps ne doivent pas avoir d’impacts entre elles.

Durabilité : les résultats des transactions commitées sont permanents. Ils doivent survivre à un crash ou à un redémarrage du système.

L’un des avantages des EJB est la possibilité pour le développeur de ne pas avoir à définir les limites des transactions (begin, commit, rollback). C’est le mode managé (ou CMT : Container­Managed Transaction) : le container gère les transactions et le développeur peut spécifier les méthodes qui sont engagées dans une transaction.

À l’inverse, dans un mode non managé (ou BMT : Bean­Managed Transaction), le développeur gère les transactions de bout en bout.

La JTA (Java Transaction API) est le composant central des EJB pour la gestion des transactions par le container. Elle inclue l’interface javax.transaction.UserTransaction qui permet au développeur de spécifier les limites des transactions lorsqu’il décide de développer en mode managé (par le container) mais souhaite avoir un contrôle sur les transactions.

La Java Persistence API peut être utilisée dans un environnement où la JTA n’est pas supportée. Dans ce cas la gestion des transactions passe par une interface alternative : EntityTransaction. Cette interface est utilisée pour contrôler les transactions d’entity managers en mode resource­local (c’est­à­dire hors container).

La Java Persistence API n’a pas besoin d’être utilisée dans un container qui supporte la JTA. En fait la méthode EntityManager.getTransaction(), qui renvoie un objet EntityTransaction, ne devrait pas être appelée à partir d’un EntityManager avec un type de transaction JTA. Autrement dit, il ne faut pas utiliser la méthode getTransaction() si dans le fichier persistence.xml l’attribut transaction­type est défini comme JTA.

Ceci impacte les entity managers :

Si l’entity manager est obtenu avec la méthode createEntityManager(), de l’interface EntityManagerFactory, alors les transactions sont managées par l’application (le développeur). Ce qui signifie qu’il est nécessaire d’appeler la méthode EntityManager.joinTransaction() pour participer à une transaction.

Si l’entity manager est obtenu par injection, alors les transactions sont managées par le container et l’entity manager est déjà dans une transaction.

Une application EJB 3 peut fonctionner en mode non managé. Dans ce cas l’interface EntityManagerFactory assure la connexion à la base et le développeur défini les limites des transactions.

On distingue deux modes de démarcation des transactions dans les EJB : les transactions managées par le container et les transactions managées par le développeur du bean.

Java EE supporte les transactions distribuées (des mises à jour peuvent se faire sur plusieurs bases de données) mais ne supporte pas les transactions imbriquées (une transaction ne peut démarrer à l’intérieur

d’une autre transaction).

1. Les transactions managées par le container (mode déclaratif)

Dans ce mode, le container démarre et commite les transactions automatiquement. On parle également de mode CMT (Container­Managed Transaction). Dans ce mode, il existe 6 attributs applicables au niveau des méthodes et des classes pour gérer les transactions et leurs propagations. Les attributs qui sont spécifiés au niveau des méthodes doivent prendre en compte l’attribut de la classe dans laquelle elles sont définies. L’annotation qui sert à spécifier le

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 49: Ejb Jsf Struts Flex Jasper

type de transaction au niveau d’une méthode est @TransactionAttribute :

@TransactionAttribute(value=XXX) public void nomMethode() ...

où le type de transaction XXX peut être l’un des suivants :

Mandatory : cet attribut indique qu’une méthode doit obligatoirement être appelée dans une transaction. Sinon une exception est générée.

Required : cet attribut requiert une transaction. S’il n’y a pas de transaction alors une nouvelle transaction est créée. C’est la valeur par défaut d’une méthode dans le mode déclaratif.

Requires_New : cet attribut requiert la création d’une nouvelle transaction. S’il existe déjà une transaction, celle­ci est suspendue.

Supports : cet attribut indique qu’une transaction est supportée si elle existe mais elle n’est pas obligatoire.

Not_Supported : les transactions ne sont pas supportées.

Never : c’est l’inverse de l’attribut mandatory. La méthode ne doit jamais être exécutée dans une transaction. S’il en existe une, une exception est levée.

L’annotation @TransactionAttribute peut s’appliquer aussi bien à des méthodes qu’à des classes.

2. Les transactions managées par le développeur (mode programmatique)

Dans le mode programmatique, appelé également BMT (Bean­Managed Transaction), le container fournit encore la transaction mais le développeur peut démarrer lui­même une transaction (begin), la terminer (commit) ou revenir en arrière (rollback). L’annotation à utiliser dans ce cas au niveau de la classe est la suivante :

@TransactionManagement(TransactionManagementType.BEAN)

Seuls les sessions et message­driven beans peuvent utiliser le mode transactionnel BEAN. Par défaut, la valeur de l’attribut TransactionManagement est CONTAINER. Le mode CONTAINER est donc le mode par défaut des beans.

Dans le mode BEAN, l’interface javax.transaction.UserTransaction est utilisée pour démarrer et commiter une transaction. Les méthodes de cette interface sont :

void begin() : création d’une nouvelle transaction.

void commit() : terminer la transaction.

int getStatus() : obtenir le status d’une transaction.

void rollback() : rollbacker la transaction.

void setRollbackOnly() : la seule issue possible de la transaction est un rollback.

void setTransactionTimeout(int secondes) : modifier le timeout de la transaction.

3. Transactions et exceptions

Quand des exceptions se produisent, les comportements des clients des beans varient en fonction des types d’exceptions. Il existe deux types d’exceptions dans Java en général :

Checked exceptions : ce sont des exceptions attendues et vérifiées par le compilateur. Elles doivent être

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 50: Ejb Jsf Struts Flex Jasper

catchées dans un bloc try­catch ou bien être présentes dans les clauses throws des signatures des méthodes qui peuvent lancer de telles exceptions. Elles dérivent de la classe java.lang.Exception.

Unchecked exceptions : le compilateur ne les vérifie pas. Elles sont détectées à l’exécution et dérivent des classes java.lang.Error (par exemple OutofMemoryError) et java.lang.RuntimeException. La clause throws est optionnelle.

Dans les EJB, les exceptions sont distinguées suivant deux catégories :

System exceptions : ce sont des exceptions inattendues, par exemple une erreur de connexion à la base. Elles correspondent aux exceptions qui sont des classes filles de java.lang.RuntimeException et java.rmi.RemoteException. Parmi ces exceptions se trouvent javax.ejb.EJBException, javax.ejb.EJBAccessException et java.lang.NullPointerException. Elles sont loggées par le serveur.

Application exceptions : ce sont des checked et unchecked exceptions. Elles sont attendues et donc gérées par le client.

La table suivante décrit l’impact d’une exception sur une transaction suivant les types d’exceptions rencontrées et les types de gestion des transactions :

Pour définir une exception de type Application exception, l’API EJB 3 met à notre disposition l’annotation @javax.ejb.ApplicationException. L’attribut rollback permet au développeur de décider si la transaction doit être rollbackée (true) ou non (false, valeur par défaut) :

import javax.ejb.ApplicationException; @ApplicationException(rollback=true) public class MonException extends Exception ...

La transaction est automatiquement rollbackée lorsque l’exception MonException est levée.

4. Création d’un client web

Il est commun d’avoir dans la même archive EAR (Enterprise Application Archive) un module EJB et un module Web. Dans ce cas, le module Web fonctionne en tant que client du module EJB.

Dans cet exemple, vous allez écrire un simple module Web constitué d’une page JSP qui appelle une servlet. Une servlet est une classe qui traite et répond à des requêtes HTTP. Cette classe étend la classe javax.servlet.GenericServlet ou la classe javax.servlet.http.HttpServlet. La servlet réalise la connexion avec le module EJB et affiche dans la console les messages de l’EJB.

Il faut rendre les EJB visibles au client Web, autrement dit il faut lier le projet Web au projet EJB. Pour cela, faites un clic droit sur le projet, allez dans les propriétés puis dans Java EE Module Dependencies. Cochez la check box du module VenteEnLigneEJB.jar.

Application exceptions System exceptions

Transaction managée par le développeur

La transaction continue (sauf si le bean appelle la méthode setRollbackOnly()).

Rollback automatique. L’instance du bean est détruite.

Transaction managée par le container

La transaction continue. Rollback automatique. L’instance du bean est détruite.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 51: Ejb Jsf Struts Flex Jasper

Cliquez sur OK. Le module EJB est désormais visible dans le projet Web. Toujours au niveau du projet VenteEnLigneWeb, faites un clic droit sur WebContent ­ New ­ JSP. Créez une JSP index.jsp. À l’intérieur de cette JSP, appelez la Servlet TestTransactionBMT, qu’il faudra écrire par la suite.

Le code de la servlet TestTransactionBMT.java est le suivant :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Client EJB</title> </head> <body> <h1>TestTransactionBMT</h1> <a href="TestTransactionBMT">Cliquez ici pour appeler la servlet TestTransactionBMT</a> </body> </html>

Pour créer la servlet TestServlet dans le package, faites un clic droit sur Java Resources : src ­ New ­ Servlet.

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 52: Ejb Jsf Struts Flex Jasper

Le code de la servlet TestTransactionBMT.java est le suivant :

package com.eni.dvtejb.web; import java.io.IOException; import java.math.BigDecimal; import javax.annotation.Resource; import javax.ejb.TransactionManagement; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Utilisateur;

@TransactionManagement(javax.ejb.TransactionManagementType.BEAN)

public class TestTransactionBMT extends javax.servlet.http.HttpServlet static final long serialVersionUID = 1L; @Resource private javax.transaction.UserTransaction userTx; private EntityManagerFactory emFactory; private EntityManager em; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException try emFactory =

Persistence.createEntityManagerFactory("VenteEnLigneClientJavaEE");

em = emFactory.createEntityManager();

userTx.begin();

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 53: Ejb Jsf Struts Flex Jasper

em.joinTransaction();

//Ajout d’une adresse Adresse adresse = new Adresse(); BigDecimal codePostal = new BigDecimal("75110"); adresse.setCodepostal(codePostal); adresse.setDepartement("Paris"); BigDecimal numero = new BigDecimal("45"); adresse.setNumero(numero); adresse.setPays("France"); adresse.setRue("Lafayette"); adresse.setVille("Paris"); em.persist(adresse);

//Ajout d’un client Utilisateur client = new Utilisateur (); client.setEmail("[email protected]"); BigDecimal fax = new BigDecimal("1115333"); client.setFax(fax); client.setLogin("azerty"); client.setPassword("qwerty"); client.setNom("Dabet"); client.setPrenom("Jean"); BigDecimal telephone = new BigDecimal("4567899"); client.setTelephone(telephone); client.setTitre("Mr"); client.setAdresseFk(adresse); em.persist(client);

userTx.commit();

catch (Exception e) e.printStackTrace();

5. Création du fichier persistence.xml dans le projet VenteEnLigneClient

L’unité de persistance utilisée cette fois­ci est VenteEnLigneModuleEJB. Le fichier de persistance utilisé est présent dans le projet VenteEnLigneEJB dans le répertoire /META­INF/.

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.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_1_0.xsd"> <persistence-unit name="VenteEnLigneModuleEJB" transaction-type="JTA">

<provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/OracleDS</jta-data-source>

<class>com.eni.dvtejb.metier.entities.Utilisateur</class> <class>com.eni.dvtejb.metier.entities.Adresse</class> <class>com.eni.dvtejb.metier.entities.Commande</class> <class>com.eni.dvtejb.metier.entities.Lignecommande</class> <class>com.eni.dvtejb.metier.entities.Article</class> <class>com.eni.dvtejb.metier.entities.Produit</class> <class>com.eni.dvtejb.metier.entities.Catalogue</class> <properties>

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 54: Ejb Jsf Struts Flex Jasper

<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/> <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" />

<property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>

Il est nécessaire d’ajouter une balise <jta­data­source>.

Elle fait référence au nom JNDI de la data source (Oracle), qui provient du nom JNDI du fichier deploy/oracle­ds.xml, et qui est utilisée par le container.

6. Contenu du descripteur de déploiement web.xml

Lorsque le serveur web reçoit une requête, il détermine la classe de la servlet à appeler grâce à un fichier de configuration. Ce fichier de configuration est un descripteur de déploiement de l’application web et s’appelle web.xml. Il réside dans le répertoire /WEB­INF.

<?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_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>VenteEnLigneWeb</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> <description></description> <display-name>TestTransactionBMT</display-name> <servlet-name> TestTransactionBMT </servlet-name> <servlet-class>com.eni.dvtejb.web.TestTransactionBMT </servlet-class>

</servlet> <servlet-mapping> <servlet-name> TestTransactionBMT </servlet-name> <url-pattern>/TestTransactionBMT </url-pattern> </servlet-mapping> </web-app>

Ce fichier web.xml déclare une servlet nommée TestTransactionBMT et la mappe à l’URL dont le chemin est /TestTransactionBMT.

Le tag <welcome­file­list> désigne une liste de fichiers que le serveur vérifie et affiche si l’utilisateur entre une URL qui n’est pas mappée à une servlet et qui correspond à un chemin de répertoire.

Si vous changez le fichier web.xml, vous devez arrêter et démarrer le serveur pour voir les changements.

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpiI+fFhH82ICwA=-enidentnumber

Page 55: Ejb Jsf Struts Flex Jasper

Le mapping des relations

Les relations entre les tables sont définies grâce aux annotations suivantes :

@OneToOne, @OneToMany, @ManyToOne et @ManyToMany

Ces relations peuvent être unidirectionnelles ou bidirectionnelles.

Dans une relation unidirectionnelle, une seule entité possède un champ relationnel ou une propriété qui fait référence à l’autre entité. L’entité A référence l’entité B, mais l’entité B ne fait pas référence à l’entité A.

Dans une relation bidirectionnelle, chaque entité a un champ relationnel ou une propriété qui fait référence à l’autre entité.

Les stratégies d’implémentation pour chacune de ces relations sont diverses et leurs choix dépendent principalement des performances.

Dans une relation entre deux tables, on fait la distinction entre la table qui possède la relation et la table inverse. La table qui possède la relation est habituellement celle qui possède la clé étrangère.

1. Les relations OneToOne

C’est le cas par exemple de la table Utilisateur avec la table Adresse.

Le choix ici a été de déclarer une clé étrangère dans la table UTILISATEUR, c’est­à­dire la table qui possède la relation. Cette clé étrangère pointe sur la clé primaire de la table ADRESSE.

Au niveau des entités, la relation est reflétée dans les classes de la façon suivante :

@Entity public class Client implements Serializable ... @JoinColumn (name="ADRESSE_FK")

@OneToOne private Adresse adresseFk; ... @Entity public class Adresse implements Serializable ...

Dans le cas d’une relation bidirectionnelle, il faut ajouter dans la classe inverse l’attribut mappedBy au champ qui fait référence à la classe qui possède la relation.

@Entity public class Commande implements Serializable ... @OneToOne (mappedBy="commande")

private Compte compte; ...

Unidirectionnelle

Bidirectionnelle

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjXHCQhiH82ICwA=-enidentnumber

Page 56: Ejb Jsf Struts Flex Jasper

@Entity public class Facture implements Serializable ... @OneToOne private Commande commande;

2. Les relations OneToMany et ManyToOne

Il y a deux façons de mapper des relations one­to­many, soit avec une table de jointure (liaison unidirectionnelle), soit en définissant une colonne en tant que clé étrangère, au niveau de la classe côté many (liaison bidirectionnelle).

L’entité Utilisateur possède une relation many­to­one avec l’entité Adresse. L’annotation @ManyToOne est utilisée dans l’entité qui représente la partie « plusieurs » de la relation.

Entité Utilisateur

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name="ADRESSE_FK") private Adresse adresseFk;

Entité Adresse

@OneToMany(mappedBy="adresseFk") private Set<Utilisateur> clientCollection;

L’annotation @ManyToOne possède les attributs FETCH et CASCADE :

Attribut FETCH

Cet attribut déclare la façon dont les données sont chargées depuis la base. En spécifiant la valeur EAGER à l’attribut FETCH, le container charge en mémoire l’entité Adresse et il est ainsi possible d’y accéder directement par le getter getAdresseFk() sur une instance de l’entité Utilisateur.

L’autre valeur possible est LAZY (chargement retardé). Dans ce cas, l’entité Adresse est chargée quand elle est accédée. Les modes de chargement des entités sont donc de type EAGER ou LAZY. Ce dernier étant le mode par défaut. La différence entre ces deux modes peut être expliquée par le code de l’entité A suivante :

@Entity public class A implements Serializable @Id private long id; @Column(name="B") private B b; // B est une entité @Column(name="C") private C c; // C est une autre entité ...

Dans le mode LAZY, au chargement d’une entité dans le contexte JPA (suite à une requête par exemple), seuls les champs de l’entité A sont chargés en mémoire. Les entités B et C ne seront chargées que lors de leur première utilisation. Alors que dans le mode EAGER, les champs de l’entité A sont chargés ainsi que les entités B et C. Toutes les trois sont immédiatement chargées. En général, le mode EAGER (chargement immédiat) est adapté aux applications Web à partir du moment où les données des tables en base ne sont pas trop importantes et en prenant en compte également la mémoire disponible sur le serveur.

Attribut CASCADE

Les valeurs possibles de cet attribut sont : PERSIST, REMOVE, REFRESH, MERGE, ALL.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjXHCQhiH82ICwA=-enidentnumber

Page 57: Ejb Jsf Struts Flex Jasper

Cet attribut permet de spécifier le niveau de propagation souhaité sur les entités liées à une entité dite cible. C’est donc une persistance en cascade.

Ainsi, l’entité ADRESSE sera mise à jour (MERGE) quand l’entité UTILISATEUR sera mise à jour.

3. Les relations ManyToMany

C’est le cas de la relation entre une Commande et une Ligne de commande :

Entité Commande

@ManyToMany @JoinTable(name="LIGNECOMMANDE_TJ", joinColumns=@JoinColumn(name="COMMANDE_FK"), inverseJoinColumns=@JoinColumn(name="LIGNECOMMANDE_FK")) private Set<Lignecommande> lignecommandeCollection;

Entité LigneCommande

@ManyToMany(mappedBy="lignecommandeCollection") private Set<Commande> commandeCollection;

Voici un tableau récapitulatif des relations présentes dans l’application :

Relation Partie qui contient la relation Multiplicité Type de Fetch

Utilisateur → Adresse Utilisateur ManyToOne EAGER

Utilisateur → Commande Commande OneToMany LAZY

Article → Produit Article ManyToOne LAZY

Article → Stock Article OneToOne LAZY

Article → LigneCommande LigneCommande OneToMany

Produit → Catalogue Produit ManyToOne LAZY

Commande → Adresse Commande ManyToOne LAZY

Commande → Utilisateur Commande ManyToOne LAZY

Commande → LigneCommande Aucune (table de jointure) ManyToMany LAZY

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjXHCQhiH82ICwA=-enidentnumber

Page 58: Ejb Jsf Struts Flex Jasper

Les stratégies d’héritage

Il existe trois stratégies d’héritage possibles pour mapper des classes à des tables. Chaque stratégie présente des avantages et des inconvénients.

1. Une table unique

Dans cette stratégie, toutes les classes sont mappées à une seule table. C’est la stratégie par défaut et c’est celle qui a été choisi pour mapper les entités Client, Administrateur et Gestionnaire.

La table Utilisateur possède une colonne type_util qui joue le rôle de discriminateur.

Ce discriminateur permet de mapper la table vers trois sous­classes de la classe Utilisateur grâce à ce discriminateur. Les valeurs possibles sont C, A et G.

Modifiez l’accesseur de la classe Utilisateur pour la rendre abstraite et ajoutez les annotations @Inheritance et @DiscriminatorColumn :

@Entity @SequenceGenerator(name="SeqUtilisateur", sequenceName="utilisateur_seq") @Table(name="UTILISATEUR", schema="HR") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="TYPE_UTIL", discriminatorType=DiscriminatorType.STRING, length=1) abstract class Utilisateur implements Serializable ...

Le champ discriminatoire étant déclaré dans cette classe abstraite, il faut supprimer le champ type_util et ses accesseurs (getter et setter) de la classe Utilisateur.

Créez ensuite les trois sous­classes qui correspondent aux entités Client, Administrateur et Gestionnaire et annotez­les en spécifiant la valeur de la colonne discriminante à l’aide de l’annotation @DiscriminatorValue. La stratégie d’héritage (SINGLE_TABLE) est décrite dans l’attribut strategy de l’annotation @Inheritance.

Entité Client :

package com.eni.dvtejb.metier.entities; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @Entity @DiscriminatorValue(value="C")

public class Client extends Utilisateur

Entité Administrateur :

package com.eni.dvtejb.metier.entities; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @Entity @DiscriminatorValue(value="A")

public class Administrateur extends Utilisateur

Entité Gestionnaire :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgenSRZiH82ICwA=-enidentnumber

Page 59: Ejb Jsf Struts Flex Jasper

package com.eni.dvtejb.metier.entities; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @Entity @DiscriminatorValue(value="G")

public class Gestionnaire extends Utilisateur

Modifiez également les deux fichiers persistence.xml (dans les projets VenteEnLigneClient et VenteEnLigneEJB) pour y ajouter les trois entity beans précédemment créés :

<persistence-unit name="VenteEnLigneClientTest" transaction-type="JTA">

... <class>com.eni.dvtejb.metier.entities.Utilisateur</class> <class>com.eni.dvtejb.metier.entities.Adresse</class> <class>com.eni.dvtejb.metier.entities.Commande</class> <class>com.eni.dvtejb.metier.entities.Lignecommande</class> <class>com.eni.dvtejb.metier.entities.Article</class> <class>com.eni.dvtejb.metier.entities.Produit</class> <class>com.eni.dvtejb.metier.entities.Catalogue</class> <class>com.eni.dvtejb.metier.entities.Client</class> <class>com.eni.dvtejb.metier.entities.Administrateur</class> <class>com.eni.dvtejb.metier.entities.Gestionnaire</class> ... </persistence-unit>

2. Une table par classe

Dans cette stratégie, chaque classe est mappée à une table séparée. C’est le cas des autres tables du modèle. Cette stratégie est déclarée avec l’annotation @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

Entité Adresse :

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.math.BigDecimal; import java.util.Set; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator; @Entity @SequenceGenerator(name="SeqAdresse", sequenceName="adresse_seq") @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public class Adresse implements Serializable ...

3. Une table par classe fille

Une table unique contient tous les champs communs qui se trouvent dans une classe mère, alors que les classes filles avec leurs champs spécifiques sont mappées sur des tables séparées.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgenSRZiH82ICwA=-enidentnumber

Page 60: Ejb Jsf Struts Flex Jasper

Les tables des classes filles sont reliées à la table de la classe mère par des clés primaires. Une colonne discriminante est aussi utilisée dans cette stratégie. Elle détermine les valeurs des clés primaires.

L’annotation de cette stratégie est la suivante : @Inheritance(strategy=InheritanceType.JOINED)

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgenSRZiH82ICwA=-enidentnumber

Page 61: Ejb Jsf Struts Flex Jasper

Le langage JPQL

Le langage JPQL (Java Persistence Query Language) est une extension d’EJB QL (Enterprise JavaBean Query Language, une nouveauté introduite dans les spécifications 2.0), proche de la syntaxe SQL mais orienté objet. En effet, les requêtes JPQL opèrent sur des classes et des objets persistés (entités) au lieu de tables et colonnes. Elles renvoient des entités JPA.

JPQL fait partie de la spécification JPA (JSR 220) et améliore EJB QL en fournissant plus d’options :

les projections,

les requêtes paramétrées,

les sous­requêtes,

les requêtes dynamiques,

les clauses GROUP BY et HAVING,

l’opérateur JOIN,

DELETE et UPDATE,

SQL natif.

L’interface EntityManager vue précédemment fournit la méthode createQuery() pour créer des requêtes. Cette méthode retourne une instance Query.

Les requêtes sont exécutées quand les méthodes getSingleResult(), getResultList() ou executeUpdate() de l’interface javax.persistence.Query sont appelées.

Pour exécuter une requête de suppression (DELETE) ou de mise à jour (UPDATE), il faut utiliser la méthode executeUpdate(). Elle retourne le nombre d’objets modifiés.

@PersistenceContext em; Query query = em.createQuery("DELETE FROM Article a"); int nbArticlesSupprimes = q.executeUpdate();

La méthode getSingleResult() est à utiliser lorsque le résultat attendu de la requête retourne 0 ou 1 ligne. Elle retourne un objet unique. Si plus d’une ligne est retournée, alors une exception est générée.

@PersistenceContext em; Query query = em.createQuery("SELECT a FROM Article a WHERE a.nom = :valeur"); q.setParameter("valeur", articleNom); Article art = q.getSingleResult();

Les requêtes suivantes utilisent les entités Client, Catalogue, Produit, Article, Commande, Adresse.

Les opérations d’insertion INSERT ne sont pas supportées par JPQL. De nouvelles lignes peuvent être insérées en base en utilisant la méthode EntityManager.persist().

Pour faciliter l’écriture de requêtes JPQL, vous pouvez facilement mettre en place un requêteur sous la forme d’une page JSP à l’intérieur de l’application Web VenteEnLigneWeb.

Pour cela créez la classe /VenteEnLigneWeb/src/com/eni/dvtejb/web/EntityManagerSingleton.java suivante :

package com.eni.dvtejb.web; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence;

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 62: Ejb Jsf Struts Flex Jasper

public class EntityManagerSingleton private static EntityManager entityManager = null; public static EntityManager getInstance() if ( entityManager == null) EntityManagerFactory entityManagerFactory; entityManagerFactory = Persistence.createEntityManagerFactory("VenteEnLigneClientJavaSE"); entityManager = entityManagerFactory.createEntityManager(); return entityManager; else return entityManager;

Le rôle de cette classe est de fournir une instance unique de la classe EntityManager, conformément au design pattern Singleton. L’unité de persistance VenteEnLigneClientJavaSE a un type de transaction qui est RESOURCE_LOCAL.

Puis créez la JSP /VenteEnLigneWeb/WebContent/requeteur.jsp suivante :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="javax.persistence.*,java.util.*,com.eni.dvtejb.web.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Requêteur</title> </head> <body> <center> <h1>Requêteur JPQL</h1> <form action="requeteur.jsp" method="post"> <b>Tapez la requête : </b> <br/> <textarea name="requete" rows="10" cols="70">$param.requete</textarea><br/> <input type="submit" value="Lancer la requête JPQL"/> </form> <p/> <% String requete = request.getParameter("requete"); if ( requete == null || requete.length() == 0) return; EntityManager em = EntityManagerSingleton.getInstance(); List liste = null; try Query req = em.createQuery(requete); liste = req.getResultList(); catch(Exception ex) out.println("<p/>Erreur : " + ex.getMessage()); return; out.println("<h2>Resultat </h2><table border=’2’"); for (Object obj : liste) out.println("<tr>"); if (obj instanceof Object[]) for( Object o : (Object[]) obj) out.println( "<td>" + o + "</td>"); else out.println("<td>" + obj.toString() + "</td>");

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 63: Ejb Jsf Struts Flex Jasper

out.println("</tr>"); %> </center> </body> </html>

Le résultat de la requête JPQL s’affiche sur la même page :

1. La clause SELECT et les requêtes dynamiques

Récupération de tous les clients :

@PersistenceContext em; public List findTousClients() Query requete = em.createQuery("SELECT c FROM Client c"); return requete.getResultList();

Cette requête peut également s’écrire sans le mot­clé SELECT :

Query requete = em.createQuery("FROM Client");

Récupération des clients dont le nom est Bock :

@PersistenceContext em; Query requete = em.createQuery("SELECT c FROM Client c WHERE nom = ’Bock’"); List<Client> resultats = requete.getResultList(); for (Client p : resultats) System.out.println(p.getPrenom());

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 64: Ejb Jsf Struts Flex Jasper

Récupération des clients dont les noms commencent par la lettre B :

@PersistenceContext em; Query reqLettre = em.createQuery("SELECT c FROM Client c WHERE nom LIKE ’B%’"); List<Client> resultatsreqLettre = reqLettre.getResultList(); for (Client p : resultatsreqLettre) System.out.println(p.getNom());

Liste des types de cartes de crédit :

@PersistenceContext em; Query reqDistinct = em.createQuery("SELECT DISTINCT co.typeCartecredit FROM Commande co " ); List<String> typesCarteCredit = reqDistinct.getResultList(); for (String typeCarteCredit : typesCarteCredit) System.out.println(typeCarteCredit);

Cette requête renvoie une liste de chaînes de caractères représentant les types de carte de crédit trouvés dans la table COMMANDE :

@PersistenceContext em; Query reqJoin = em.createQuery("SELECT a FROM Article a, IN(a.produitFk) prod WHERE prod.nom = ’Foot’" ); List<Article> resultatsreqJoin = reqJoin.getResultList(); for (Article a : resultatsreqJoin) System.out.println(a.getNom());

2. La clause SELECT NEW

La clause SELECT NEW représente une utilisation avancée de JPQL. C’est une expression JPQL sur un constructeur. Elle renvoie des instances d’une classe qui correspondent aux données retournées par la requête. Elle est particulièrement adaptée aux objets de type DTO (Data Transfer Object). Son utilisation est détaillée dans le chapitre Développement d’un client avec JSF 2.

La requête suivante retourne la liste des noms des articles avec les descriptions des catalogues et produits correspondants :

public List<MagasinDTO> findBoutique() String requete = " SELECT NEW com.eni.dvtejb.metier.dtos.DescriptionDTO(c.description, p.description, a.nom) " + " FROM Catalogue c, Produit p, Article a" + " WHERE c.catalogueid = p.catalogueFk" + " AND p.produitid = a.produitFk"; return entityManager.createQuery(requete).getResultList();

Chaque ligne retournée par la requête est stockée dans une instance de la classe DescriptionDTO.

3. L’opérateur JOIN

Utilisation de l’opérateur JOIN pour récupérer la liste des articles du produit Foot :

L’opérateur JOIN remplace l’opérateur IN de la clause FROM des EJB 2. L’opérateur IN est toujours supporté.

@PersistenceContext em;

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 65: Ejb Jsf Struts Flex Jasper

Query reqJoin = em.createQuery("SELECT a FROM Article a JOIN a.produitFk prod WHERE prod.nom = ’Foot’" ); List<Article> resultatsreqJoin = reqJoin.getResultList(); for (Article a : resultatsreqJoin) System.out.println(a.getNom());

L’opérateur JOIN permet de faire le produit cartésien des lignes des tables Article et Produit.

Utilisation de l’opérateur INNER JOIN pour récupérer les noms des clients qui sont des hommes et dont l’adresse a un code postal > 77000 :

@PersistenceContext em; Query reqInnerJoin = em.createQuery("SELECT c FROM Client c INNER JOIN c.adresseFk ad WHERE c.titre = ’Mr’ AND ad.codepostal > 77000" ); List<Client> clients = reqInnerJoin.getResultList(); for (Client client : clients) System.out.println(client.getNom());

L’opérateur JOIN est équivalent à l’opérateur INNER JOIN. En effet une jointure est une jointure interne par défaut.

Les mots­clés ne sont pas case­sensitives. En revanche les noms des variables sont case­sensitives. Vous obtiendrez une erreur si vous tentez de faire une requête sur l’entité client avec un c minuscule au lieu d’un

C majuscule.

Liste de toutes les commandes issues des clients dont l’id est supérieur à 2 :

@PersistenceContext em; Query reqLeftJoin = em.createQuery("SELECT co FROM Commande co LEFT JOIN co.clientFk c WHERE c.clientid > 2" ); List<Commande> commandes = reqLeftJoin.getResultList(); for (Commande commande : commandes) System.out.println(commande.getCommandeid());

L’opérateur LEFT JOIN est similaire à l’opérateur LEFT OUTER JOIN.

Utilisation d’une sous­requête pour sélectionner les commandes du client dont le nom est Gatersa :

@PersistenceContext em; Query reqSousReq = em.createQuery("SELECT DISTINCT co FROM Commande co WHERE EXISTS " + " (SELECT cl FROM co.clientFk cl WHERE cl.nom =’Gatersa’ )"); List<Commande> listeCommandes = reqSousReq.getResultList(); for (Commande commande : listeCommandes) System.out.println(commande.getCommandeid());

Les sous­requêtes sont autorisées dans les clauses HAVING et WHERE.

4. Les paramètres

Les paramètres des requêtes peuvent être nommés ou numérotés. Ils sont indiqués selon deux notations :

avec des points d’interrogation suivis de chiffres pour préciser leur position :

@PersistenceContext em; Query reqPosition = em.createQuery("SELECT c.prenom FROM Client c WHERE c.nom = ?1 AND c.titre = ?2 ");

reqPosition.setParameter(1, "Duc");

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 66: Ejb Jsf Struts Flex Jasper

reqPosition.setParameter(2, "Mr"); List<String> listePrenomsPt = reqPosition.getResultList(); for (String prenom : listePrenomsPt) System.out.println(prenom);

Leur position commence à partir de 1.

avec deux points suivis du nom du paramètre :

@PersistenceContext em; Query reqDeuxPoints= em.createQuery("SELECT c.prenom FROM Client c WHERE c.nom = :nomChoisi ");

reqDeuxPoints.setParameter("nomChoisi", "Duc"); List<String> listePrenoms = reqDeuxPoints.getResultList(); for (String prenom : listePrenoms) System.out.println(prenom);

5. Les agrégateurs HAVING et GROUP BY

Sélection des titres des clients et de leur nombre en utilisant les agrégateurs HAVING et GROUP BY :

@PersistenceContext em; Query reqGroup = em.createQuery("SELECT c.titre, count(c) FROM Client c GROUP BY c.titre HAVING COUNT(c)> 0"); List resultatsGroup = reqGroup.getResultList(); for (Iterator it = resultatsGroup.iterator(); it.hasNext();) Object[] obj = (Object[]) it.next(); for (int i=0;i<obj.length;i++) System.out.println(obj[i]);

6. Les requêtes nommées

Il est possible de définir des requêtes de façon statique avec les named queries, c’est­à­dire les requêtes nommées. Elles sont définies soit dans un fichier XML soit avec des annotations au niveau des entity beans.

Recherche des noms des clients qui ont pour prénom "Jules" :

Via des annotations :

Modifiez l’entity bean Client pour lui ajouter une requête nommée avec l’annotation @javax.persistence.NamedQuery :

@Entity @NamedQuery(name="Client.findByPrenom", query=" SELECT c FROM Client c WHERE c.prenom = :lePrenom") public class Client implements Serializable ...

Ensuite depuis un stateful session bean par exemple (les session beans sont détaillés dans le chapitre suivant) vous pouvez appeler cette requête nommée de la façon suivante :

public List findByPrenom(String prenom) log.debug("recherche par prenom"); Query q = em.createNamedQuery("Client.findByPrenom"); q.setParameter("lePrenom", prenom);

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 67: Ejb Jsf Struts Flex Jasper

List<Client> resultatsNamedQuery = q.getResultList(); return resultatsNamedQuery;

Pour insérer plusieurs requêtes, utilisez l’annotation @javax.persistence.NamedQueries.

Veillez à ne pas mettre d’espace à l’intérieur de l’argument de la méthode createNamedQuery(), entre les guillemets. Sinon une exception IllegalArgumentException sera générée. En effet la méthode ne supprime

pas les espaces.

Via un mapping XML dans le fichier orm.xml :

Insérez la requête dans le fichier /META­INF/orm.xml du projet VenteEnLigneEJB :

Contenu du fichier orm.xml :

<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0"> <named-query name="Client.findByPrenom "> <query> SELECT c FROM Client c WHERE c.prenom = :lePrenom</query> </named-query> </entity-mappings>

Ce nom de requête doit être unique, il ne doit pas entrer en conflit avec d’autres requêtes dans ce fichier ou des requêtes définies au niveau des annotations.

7. Les requêtes natives

La Java Persistence API offre la possibilité d’exécuter des requêtes en SQL natif, c’est­à­dire Oracle SQL si la base de données est Oracle, par l’intermédiaire de la méthode createNativeQuery() de l’interface EntityManager. Dans ce cas, les requêtes manipulent des tables, et non plus des objets.

Pour insérer un nouveau catalogue dans la table Catalogue, la séquence suivante est mise en place dans Oracle :

create sequence catalogue_seq minvalue 1 start with 1 increment by 1

La requête d’insertion peut alors être la suivante :

public static final String INSERE_CATALOGUE = "INSERT INTO catalogue (catalogueid, description, nom) VALUES (catalogue_seq.nextval, ?1, ?2)"; ... Query reqNative = em.createNativeQuery(INSERE_CATALOGUE); reqNative.setParameter(1,"Pour les litteraires"); reqNative.setParameter(2, "Littérature"); EntityTransaction tx = em.getTransaction(); tx.begin(); reqNative.executeUpdate(); tx.commit();

8. Les requêtes polymorphiques

JPA supporte les requêtes polymorphiques : un SELECT sur l’entité Utilisateur ramène les lignes de l’entité Utilisateur ainsi que les lignes des classes filles (Client, Administrateur, Gestionnaire).

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 68: Ejb Jsf Struts Flex Jasper

La requête suivante renvoie tous les noms des utilisateurs :

Query reqPoly = em.createQuery("SELECT u.prenom FROM Utilisateur u"); List<String> resultatsPoly = reqPoly.getResultList(); for (String prenom : resultatsPoly) System.out.println(prenom);

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmDCoCJiH82ICwA=-enidentnumber

Page 69: Ejb Jsf Struts Flex Jasper

Introduction

Les EJB sont des composants accessibles localement (même JVM) ou à distance (différentes JVM) via un lookup JNDI. Une référence à un composant EJB est similaire à un objet proxy. Chaque méthode de l’objet référencé est routée vers la même méthode du composant EJB d’origine.

Si le composant EJB est défini de façon locale, le routage est fait directement via reflection et les paramètres et le résultat sont passés par référence. Si le composant EJB est défini de façon distante (remote), le routage est fait via rpc et les paramètres et le résultat sont passés par valeur et sont sérialisés.

Les session beans réalisent des actions et constituent généralement le point d’entrée des clients des entity beans. Ils sont divisés en deux types : les stateful session beans (beans avec états) et les stateless session beans (beans sans états). Les session beans vont contenir la logique métier pour pouvoir rechercher des produits, supprimer des articles, ajouter un client, etc.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Ml1YAjZiH82ICwA=-enidentnumber

Page 70: Ejb Jsf Struts Flex Jasper

Connexion et maintien de l’état avec un stateful session bean

Le cycle de vie d’un session bean est managé par le container et fournit des méthodes callbacks qui permettent au développeur d’agir en fonction de certains évènements. Par exemple, si vous voulez ajouter une certaine logique après la création d’un EJB par le container, il suffit simplement de coder une méthode dans la classe de l’EJB et d’annoter cette méthode avec l’annotation @PostConstruct. Ou encore si vous voulez déclencher une action juste avant la destruction d’un EJB, il suffit de coder une méthode dans la classe de l’EJB et de l’annoter avec l’annotation @PreDestroy.

Le schéma suivant décrit le cycle de vie d’un stateful session bean :

1. Méthodes callbacks

En ce qui concerne les session beans, les méthodes callback, c’est­à­dire les méthodes qui seront automatiquement appelées, sont annotées avec les annotations suivantes :

<portée> void @PostConstruct : appelée après que le container ait créé une instance de la classe bean.

<portée> void @PrePassivate : appelée juste avant que le bean soit passivé.

<portée> void @PostActivate : appelée juste après l’activation du bean.

<portée> void @PreDestroy : appelée juste après la fin de chaque méthode @Remove et avant la destruction du bean.

Ces méthodes ne sont pas obligatoires.

Il est toujours possible d’utiliser le descripteur de déploiement ejb­jar.xml. Il faut noter qu’en cas d’utilisation mixte (annotations et fichier ejb­jar.xml), les entrées du descripteur de déploiement écrasent les annotations.

Un stateful session bean conserve dans le cache l’état d’un client, tout au long des requêtes qu’il a exécuté. Il existe une instance par client.

2. Interfaces distantes et locales

Un session bean (stateless et stateful) est constitué d’une ou deux interfaces qui peuvent être locales ou distantes et d’une classe bean.

Un bean qui implémente une interface locale est plus restrictif : il n’est accessible que par un bean ou un client qui est packagé dans la même archive EAR alors qu’un bean qui implémente une interface distante est accessible par tout

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 71: Ejb Jsf Struts Flex Jasper

bean ou client qui est packagé dans la même archive EAR ou bien dans d’autres archives EAR.

Si les EJB sont invoqués à distance, c’est­à­dire à partir d’un autre serveur que celui sur lequel ils sont déployés, alors ils doivent implémenter des interfaces distantes.

Implémenter les deux interfaces (distante et locale) lors de l’écriture d’un session bean peut représenter un avantage dans la mesure où celui­ci est ainsi prêt à servir les deux types d’appel.

La distinction entre les deux types d’interface se fait également suivant la localisation de la JVM : les interfaces locales sont accessibles par des clients qui s’exécutent sur la même JVM uniquement alors que les interfaces distances sont accessibles par des clients qui s’exécutent sur la même JVM et sur une autre JVM. Si l’on prévoit de rendre disponibles les session beans pour des clients qui s’exécutent sur d’autres JVMs, il est donc préférable d’utiliser les interfaces distantes. Il faut tout de même savoir que les performances ne sont pas les mêmes : en effet les paramètres d’une interface locale sont passés par référence (et ne peuvent donc être accédés que sur la même JVM) alors que les paramètres d’une interface distante sont passés par valeur, ce qui implique de la sérialisation et donc une plus grande latence au niveau du réseau.

Le schéma suivant illustre cette distinction :

Les interfaces locales et distantes sont annotées respectivement avec les annotations @javax.ejb.Local et @javax.ejb.Remote. Mais ces annotations sont optionnelles et ne sont donc pas requises au niveau des interfaces. Par défaut, une interface qui n’est pas annotée est considérée comme une interface locale. Au niveau du bean, pour spécifier quelles interfaces celui­ci implémente, vous pouvez utiliser ces mêmes annotations en ajoutant comme attribut(s) le ou les interface(s).

Le bean n’a pas besoin d’implémenter l’interface javax.ejb.SessionBean. Vous pouvez tout simplement l’annoter avec @javax.ejb.Stateful.

Le panier d’achat peut être représenté par un stateful session bean puisqu’il doit connaître les articles qu’il contient entre chaque requête.

Créez maintenant ce stateful session bean.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 72: Ejb Jsf Struts Flex Jasper

Faites un clic droit sur le nom du projet VenteEnLigneEJB ­ New ­ Session Bean. Entrez com.eni.dvtejb.metier.sessions pour le nom du package et PanierBean pour le nom de la classe, sélectionnez les types Stateful et Remote, Local et cliquez sur Finish.

Cliquez sur Next puis Finish.

Le wizard a créé le bean PanierBean, les business interfaces remote PanierBeanRemote et locale PanierBeanLocal. Ces business interfaces sont en fait des POJI (Plain Oriented Java Interfaces).

L’interface PanierBeanRemote contient les méthodes suivantes :

package com.eni.dvtejb.metier.sessions; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.services.ArticlePanier; public interface PanierBeanRemote public Commande genererCommande(Article[] listArticles, Client client, BigDecimal[] quantites); public void ajouterArticle(Article article, BigDecimal quantite); public Collection<String> getProduits(); public Article findById(long id); ArrayList<ArticlePanier> getPanier(); public double getMontantTotal(); public void viderPanier(); public void supprimerArticle(ArticlePanier articlePanier); public List afficherHistoCommandes(Utilisateur u);

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 73: Ejb Jsf Struts Flex Jasper

public void commander(Client client, ArrayList<ArticlePanier> articlesPanier, String numCC, String typeCC, java.sql.Date expirationDate);

Le bean PanierBean implémente toutes les méthodes de cette interface. Pour un premier test, voici le code de la méthode genererCommande(). Cette méthode n’est pas utilisée par la suite dans l’application, elle n’est utilisée que dans le test pour ce chapitre.

package com.eni.dvtejb.metier.sessions; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import javax.ejb.Remote; import javax.ejb.Stateful; import javax.interceptor.Interceptors; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.entities.Lignecommande; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.services.ArticlePanier; import com.eni.dvtjeb.metier.interceptors.CommanderInterceptor; @Stateful @Remote (PanierBeanRemote.class) public class PanierBean implements PanierBeanRemote, Serializable private static final Logger log = Logger.getLogger(PanierBean.class); @PersistenceContext(unitName="VenteEnLigneModuleEJB") EntityManager em; private static final long serialVersionUID = 1L; private ArrayList<ArticlePanier> articles; // Initialisation de la liste d’articles @PostConstruct public void creation() articles = new ArrayList<ArticlePanier>(); /** * Constructeur par défaut. */ public PanierBean() public Commande genererCommande(Article[] listArticles, Client client, BigDecimal[] quantites) // A chaque article (prix, nom) est associé une ligne de commande (quantite) List<Lignecommande> ligneCommandes = new

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 74: Ejb Jsf Struts Flex Jasper

ArrayList<Lignecommande> (listArticles.length); // entityManager.getTransaction().begin(); for (int i=0;i<listArticles.length;i++) Lignecommande lignecommande = new Lignecommande(); lignecommande.setArticleFk(listArticles[i]); lignecommande.setQuantite(quantites[i]); ligneCommandes.add(lignecommande); // Une commande est composée de 1 à N lignes de commande Commande commande = new Commande(); commande.setUtilisateurFk(client); Date aujourdhui = new Date(); long t = aujourdhui.getTime(); java.sql.Date aujourdhuiSQL = new java.sql.Date(t); commande.setDatecommande( aujourdhuiSQL); Set<Lignecommande> set = new HashSet<Lignecommande>(); set.addAll( ligneCommandes ); commande.setLignecommandeCollection(set); return commande; ... // Implémentation du reste des méthodes ...

La sérialisation est utilisée pour pouvoir conserver l’état du bean (passivation, activation) mais il n’est pas nécessaire d’implémenter l’interface Serializable. En effet l’interface javax.ejb.Session hérite de l’interface javax.ejb.EnterpriseBean qui hérite déjà de java.io.Serializable. L’unité de persistance VenteEnLigneModuleEJB est définie dans le fichier persistence.xml du projet VenteEnLigneEJB.

3. Création d’un client web

Le client peut être la même page JSP index.jsp du projet VenteEnLigneWeb. Il suffit d’ajouter un lien qui appelle la Servlet TestServlet, qu’il reste à écrire par la suite.

Voici le code de la JSP index.jsp modifée :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Client EJB</title> </head> <body> <h1>Module Web utilisé en tant que client EJB</h1> <a href="TestServlet">Cliquez ici pour appeler le session bean via la servlet TestServlet</a> <h1>TestTransactionBMT</h1> <a href="TestTransactionBMT ">Cliquez ici pour appeler la servlet TestTransactionBMT</a> </body> </html>

a. Modification du fichier jboss­service.xml

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 75: Ejb Jsf Struts Flex Jasper

Le choix peut être fait de déclarer toutes les méthodes dans une interface distante. L’interface locale créée par Eclipse peut alors être supprimée. Il est donc tout à fait possible de déclarer des méthodes d’une interface distante et de les faire tourner sur un serveur d’application en local.

Pour pouvoir invoquer les EJB à distance, il est nécessaire de modifier la valeur de l’attribut CallByValue dans le fichier jboss­service.xml. Celui­ci se trouve dans le répertoire default/conf/ de JBoss. Changez la valeur à true.

<mbean code="org.jboss.naming.NamingService" name="jboss:service=Naming" xmbean-dd="resource:xmdesc/NamingService-xmbean.xml"> <!-- The call by value mode. true if all lookups are unmarshalled using the caller’s TCL, false if in VM lookups return the value by reference. --> <attribute name="CallByValue">true</attribute>

<!-- The listening port for the bootstrap JNP service. Set this to -1 to run the NamingService without the JNP invoker listening port. --> <attribute name="Port">1099</attribute> <!-- The bootstrap JNP server bind address. This also sets the default RMI service bind address. Empty == all addresses --> <attribute name="BindAddress">$jboss.bind.address</attribute> <!-- The port of the RMI naming service, 0 == anonymous --> <attribute name="RmiPort">1098</attribute> <!-- The RMI service bind address. Empty == all addresses --> <attribute name="RmiBindAddress">$jboss.bind.address</attribute> <!-- The thread pool service used to control the bootstrap lookups --> <depends optional-attribute-name="LookupPool" proxy-type="attribute">jboss.system:service=ThreadPool</depends> <!-- An example of using the unifed invoker as the transport. <depends optional-attribute-name="InvokerProxyFactory" proxy- type="attribute">jboss:service=proxyFactory,type=unified,target=Nami ng</depends> --> <depends optional-attribute-name="Naming" proxy-type="attribute">jboss:service=NamingBeanImpl</depends> </mbean>

Pour créer la servlet TestServlet dans le package com.eni.dvtejb.web, faites un clic droit sur Java Resources : src ­ New ­ Servlet.

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 76: Ejb Jsf Struts Flex Jasper

Voici le code de la servlet TestServlet.java :

package com.eni.dvdejb.web ; import java.io.IOException; import java.math.BigDecimal; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.sessions.PanierBeanRemote; public class TestServlet extends javax.servlet.http.HttpServlet static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(TestServlet.class.getName()); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException try InitialContext initialContext = new InitialContext(); PanierBeanRemote panier = (PanierBeanRemote) initialContext.lookup("VenteEnLigne/PanierBean/remote"); logger.info ("-->> lookup effectué"); logger.info (" ----------- Debut -----------"); Article article1 = new Article(); article1.setNom("gants"); article1.setPrix(22); Article article2 = new Article(); article2.setNom("chaussures"); article2.setPrix(44);

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 77: Ejb Jsf Struts Flex Jasper

Article[] listArticles = new Article[2]; listArticles[0] = article1; listArticles[1] = article2; Client client = new Client(); client.setEmail("[email protected]"); BigDecimal fax = new BigDecimal("1115333"); client.setFax(fax); client.setLogin("azerty"); client.setPassword("qwerty"); client.setNom("Dabet"); client.setPrenom("Jean"); BigDecimal telephone = new BigDecimal("4567899"); client.setTelephone(telephone); client.setTitre("Mr"); Commande commande = panier. genererCommande (listArticles, client, new BigDecimal[]new BigDecimal("3"), new BigDecimal("5")); System.out.println(commande.getCommandeid()); logger.info (commande.getClientFk().getNom()); logger.info (commande.getClientFk().getPrenom()); logger.info (" ----------- Fin -----------"); catch (NamingException e) e.printStackTrace();

b. Modification du descripteur de déploiement web.xml :

Il reste une dernière chose à faire pour pouvoir lancer le test : le mapping de la servlet TestServlet avec l’URL /TestServlet.

<servlet> <description></description> <display-name>TestServlet</display-name> <servlet-name>TestServlet</servlet-name> <servlet-class>com.eni.dvtejb.web.TestServlet</servlet-class>

</servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/TestServlet</url-pattern> </servlet-mapping>

L’appel de cette servlet permet de vérifier que l’appel de différents entity beans via un stateful session bean s’effectue correctement. La consultation des logs dans la console des logs Eclipse permet de voir que le déroulement de la classe de test s’est déroulé correctement.

4. Accès aux ressources avec le service de nommage JNDI

Le serveur d’application crée des variables dans le service de nommage JNDI (Java Naming Directory Interface). Le client passe par l’API JNDI pour y accéder. Ces variables sont stockées dans un InitialContext. La méthode lookup() retourne un Object qui doit être casté dans le type approprié.

Dans la version JAVA EE 5, le nommage JNDI par défaut des EJB n’est pas standardisé. Il est spécifique à chaque serveur. Dans JBoss, le nommage JNDI est fait selon les règles suivantes :

dans le cas d’un package JAR : <nom_classe>/<local ou remote> ;

dans le cas d’un package EAR : <nom_package_ear>/<nom_classeBean>/<local ou remote>.

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 78: Ejb Jsf Struts Flex Jasper

L’archive générée étant de type EAR, le lookup de la ressource JNDI dans la servlet TestServlet.java se fait donc de la façon suivante :

PanierBeanRemote panier = (PanierBeanRemote) initialContext.lookup("VenteEnLigne/PanierBean/remote");

Redémarrez le serveur pour que les modifications soient prises en compte. L’archive VenteEnLigne.ear est déployée vers le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\deploy.

Le contenu de l’archive VenteEnLigne.ear est le suivant :

Jar -tf VenteEnLigne.ear META-INF/ META-INF/MANIFEST.MF META-INF/application.xml VenteEnLigneClient.jar VenteEnLigneConnector.rar VenteEnLigneEJB.jar VenteEnLigneWeb.war VenteEnLigneWebJSF2.war VenteEnLigneWebStruts2.war VenteEnLigneFlexGranite.war

Le fichier MANIFEST.MF est un fichier qui peut contenir des informations sur les fichiers qui sont packagés dans une archive. Il peut par exemple contenir le nom de la classe qui constitue le point d’entrée (la classe qui contient la méthode public static void main(String[] args)) ou encore le numéro de version de l’archive.

Il sert également au déploiement d’une application, de manière à ajouter dans le classpath les librairies JAR dont dépendent d’autres librairies JAR, à l’intérieur d’une application EAR par exemple. La ligne suivante du fichier MANIFEST.MF ajoute les librairies projet1.jar et projet2.jar (séparées par des espaces) dans le classpath de l’application qui le contient :

Manifest-Version: 1.0 Class-Path: projet1.jar projet2.jar

Le fichier de déploiement META­INF/application.xml généré par Eclipse est le suivant :

<?xml version="1.0" encoding="UTF-8"?> <application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.x sd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" id="Application_ID" version="5"> <display-name> VenteEnLigne</display-name> <module> <ejb>VenteEnLigneEJB.jar</ejb> </module> <module> <web> <web-uri>VenteEnLigneWeb.war</web-uri> <context-root>VenteEnLigneWeb</context-root> </web> </module> <module> <java>VenteEnLigneClient.jar</java> </module> <module> <web> <web-uri>VenteEnLigneWebStruts2.war</web-uri> <context-root>VenteEnLigneWebStruts2</context-root> </web> </module> <module> <web> <web-uri>VenteEnLigneWebJSF2.war</web-uri> <context-root>VenteEnLigneWebJSF2</context-root> </web>

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 79: Ejb Jsf Struts Flex Jasper

</module> <module> <web> <web-uri>VenteEnLigneFlexGranite.war</web-uri> <context-root>VenteEnLigneFlexGranite</context-root> </web> </module> </application>

La console JMX de JBoss permet de s’assurer que le déploiement s’est déroulé correctement. La console est accessible à l’URL suivante : http://localhost:8085/jmx­console.

Cliquez sur le service JNDIView puis sur le bouton Invoke de l’opération list :

Vous devez voir apparaître le nom du stateful session bean PanierBean dans la liste des noms JNDI que le serveur d’application JBoss enregistre automatiquement dans l’arbre JNDI (autrement dit le service de nommage) lorsqu’il parcourt une archive EAR et y trouve des EJB.

Global JNDI Namespace

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 80: Ejb Jsf Struts Flex Jasper

+- XAConnectionFactory (class: org.jboss.jms.client.JBossConnectionFactory) +- ClusteredConnectionFactory (class: org.jboss.jms.client.JBossConnectionFactory) +- UserTransactionSessionFactory (proxy: $Proxy96 implements interface org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory) +- console (class: org.jnp.interfaces.NamingContext) | +- PluginManager (proxy: $Proxy94 implements org.jboss.console.manager.PluginManagerMBean (no security manager: RMI class loader disabled)) +- UUIDKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.uuid.UUIDKeyGeneratorFactory) +- topic (class: org.jnp.interfaces.NamingContext) +- queue (class: org.jnp.interfaces.NamingContext) | +- ExpiryQueue (class: org.jboss.jms.destination.JBossQueue) | +- DLQ (class: org.jboss.jms.destination.JBossQueue) +- persistence.units:ear=VenteEnLigne.ear,jar=VenteEnLigneEJB.jar,unitN ame=VenteEnLigne (class: org.hibernate.impl.SessionFactoryImpl) +- ConnectionFactory (class: org.jboss.jms.client.JBossConnectionFactory) +- UserTransaction (class: org.jboss.tm.usertx.client.ClientUserTransaction) +- VenteEnLigne (class: org.jnp.interfaces.NamingContext)

| +- PanierBean (class: org.jnp.interfaces.NamingContext)

| | +- remote (class: java.lang.Object)

| | +- remoteStatefulProxyFactory (proxy: $Proxy115 implements

interface org.jboss.ejb3.proxy.ProxyFactory)

+- jmx (class: org.jnp.interfaces.NamingContext) | +- invoker (class: org.jnp.interfaces.NamingContext) | | +- RMIAdaptor (proxy: $Proxy92 implements interface org.jboss.jmx.adaptor.rmi.RMIAdaptor,interface org.jboss.jmx.adaptor.rmi.RMIAdaptorExt) | +- rmi (class: org.jnp.interfaces.NamingContext) | | +- RMIAdaptor[link -> jmx/invoker/RMIAdaptor] (class: javax.naming.LinkRef) +- ClusteredXAConnectionFactory (class: org.jboss.jms.client.JBossConnectionFactory) +- TomcatAuthenticators (class: java.util.Properties) +- HiLoKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.hilo.HiLoKeyGeneratorFactory)

Cette vue JNDI est très utile, notamment quand l’erreur javax.naming.NameNotFoundException apparaît. Il suffit de parcourir l’arbre JNDI pour voir quel nom est lié à un objet. La vue JNDI est spécifique à chaque serveur d’application.

Exécutez maintenant le client Web en faisant un clic droit sur le projet VenteEnLigneWeb puis Run As ­ Run On Server.

L’archive est republiée dans JBoss et vous devez voir affichée la page index.jsp dans Eclipse :

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 81: Ejb Jsf Struts Flex Jasper

Le lien sur la page JSP permet d’appeler la servlet TestServlet qui invoque à son tour le stateful session bean PanierBean. Les messages de sortie s’affichent alors dans la console Eclipse.

22:01:53,688 INFO [SessionFactoryImpl] building session factory 22:01:53,740 INFO [SessionFactoryObjectFactory] Factory name: persistence.units:ear=VenteEnLigne.ear,jar=VenteEnLigneEJB.jar,unitN ame=VenteEnLigne 22:01:53,741 INFO [NamingHelper] JNDI InitialContext properties:java.naming.factory.initial=org.jnp.interfaces.NamingCon textFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces 22:01:53,781 INFO [SessionFactoryObjectFactory] Bound factory to JNDI name: persistence.units:ear=VenteEnLigne.ear,jar=VenteEnLigneEJB.jar,unitN ame=VenteEnLigne 22:01:53,781 WARN [SessionFactoryObjectFactory] InitialContext did not implement EventContext 22:01:53,788 INFO [SchemaExport] Running hbm2ddl schema export 22:01:53,789 INFO [SchemaExport] exporting generated schema to database 22:01:56,368 INFO [SchemaExport] schema export complete 22:01:56,368 INFO [NamingHelper] JNDI InitialContext properties:java.naming.factory.initial=org.jnp.interfaces.NamingCon textFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces 22:01:56,521 INFO [EJBContainer] STARTED EJB: com.eni.dvtejb.metier.sessions.PanierBean ejbName: PanierBean 22:01:56,673 INFO [EJBContainer] STARTED EJB: com.eni.dvtejb.metier.sessions.ShoppingCartBean ejbName: ShoppingCartBean 22:01:56,708 INFO [TomcatDeployment] deploy, ctxPath=/VenteEnLigneWeb, vfsUrl=VenteEnLigne.ear/VenteEnLigneWeb.war

22:07:14,147 INFO [STDOUT] -->> lookup effectué

22:07:14,147 INFO [STDOUT] ----------- Debut -----------

22:07:14,351 INFO [STDOUT] 0

22:07:14,351 INFO [STDOUT] Dabet

22:07:14,351 INFO [STDOUT] Jean

22:07:14,351 INFO [STDOUT] ----------- Fin -----------

5. Injection de dépendance

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 82: Ejb Jsf Struts Flex Jasper

L’injection de dépendance est un des apports majeurs de la version 3.0 des EJB. C’est une forme d’inversion de contrôle (principe d’Hollywood). L’annotation @EJB permet d’injecter une référence au stateful bean dans le code du client web. Dans JBoss, l’injection @EJB fonctionne dans les servlets, les managed beans (JSF) et dans les autres EJB. Il peut cependant être nécessaire d’utiliser l’attribut mappedName de l’annotation @EJB. En reprenant la classe de test précédente, l’injection du stateful session bean peut alors se faire de la manière suivante :

@EJB (mappedName="VenteEnLigne/PanierBean/remote")

private PanierBeanRemote panier;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException try System.out.println("-->> lookup effectué"); System.out.println(" ----------- Debut -----------"); Article article1 = new Article(); article1.setNom("gants"); article1.setPrix(22); ... Commande commande = panier.genererCommande(listArticles, client, new BigDecimal[]new BigDecimal("3"), new BigDecimal("5")); ...

Cette annotation remplace le lookup manuel. Elle évite d’utiliser l’API JNDI, d’avoir à caster le résultat du lookup et d’avoir à gérer des exceptions dans un bloc try­catch. Dans l’exemple, le container injecte la référence du bean PanierBean.

@Resource est une autre annotation utilisée pour faire de l’injection de dépendance.

@Resource

private DataSource VenteEnLigneDS; public getCommandes() // Connexion à la base Connection connexion = VenteEnLigneDS.getConnection(); ...

D’autres annotations permettent l’injection de dépendance (@PersistenceContext, @WebServiceRef, @PersistenceUnit…). Le container injecte la ressource comme l’illustre le schéma suivant :

6. Création d’un client standalone

Les EJB sont habituellement utilisés dans une architecture distribuée, mais il est possible de les appeler à travers un client standalone, c’est­à­dire en dehors d’un container, comme nous l’avons vu dans le chapitre précédent.

Dans ce cas il faut utiliser les interfaces JNDI. Le mécanisme de l’injection n’est possible que dans un environnement managé par le container. Dans le projet VenteEnLigneEJB, ajoutez un autre stateful session bean.

L’interface PanierCycle.java

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 83: Ejb Jsf Struts Flex Jasper

package com.eni.dvtejb.metier.sessions; import java.util.HashMap; public interface PanierCycle void acheter(String article, int quantite); HashMap<String, Integer> getContenuPanier(); void payer();

Le bean PanierCycleBean.java

package com.eni.dvtejb.metier.sessions; import java.io.Serializable; import java.util.HashMap; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.PostActivate; import javax.ejb.PrePassivate; import javax.ejb.Remote; import javax.ejb.Remove; import javax.ejb.Stateful; import org.jboss.ejb3.annotation.CacheConfig; @Stateful @Remote(PanierCycle.class

@CacheConfig(removalTimeoutSeconds=15, idleTimeoutSeconds=10 )

public class PanierCycleBean implements PanierCycle, Serializable private static final long serialVersionUID = 1L; private HashMap<String, Integer> panier = new HashMap<String, Integer>(); public void acheter(String article, int quantite) if (panier.containsKey(article)) int quantiteCourante = panier.get(article); quantiteCourante += quantite; panier.put(article, quantiteCourante); else panier.put(article, quantite); public HashMap<String, Integer> getContenuPanier() return panier; @Remove public void payer() System.out.println("----> A implémenter"); @PostConstruct public void postConstructExemple() System.out.println("----> Dans la methode postConstructExemple"); @PrePassivate public void prePassivateExemple()

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 84: Ejb Jsf Struts Flex Jasper

System.out.println("----> Dans la methode prePassivateExemple"); @PostActivate public void postActivateExemple() System.out.println("----> Dans la methode postActivateExemple"); @PreDestroy public void preDestroyExemple() System.out.println("----> Dans la methode preDestroyExemple");

Ce bean contient des méthodes callback c’est­à­dire des méthodes qui font partie du cycle de vie du bean et qui sont appelées automatiquement par le container.

a. Configuration du container JBoss

JBoss définit des paramètres par défaut pour les EJB dans le fichier de configuration standardjboss.xml qui se trouve dans le répertoire conf.

Par exemple, les différents paramètres relatifs aux instances de stateless session beans sont définis dans le bloc <container­configuration> suivant :

Un extrait du fichier standardjboss.xml :

<container-configuration> <container-name>Standard Stateful SessionBean</container-name> <call-logging>false</call-logging> <invoker-proxy-binding-name>stateful-unified-invoker</invoker- proxy-binding-name> <container-interceptors> <interceptor>org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor</in terceptor> <interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.security.PreSecurityInterceptor</ interceptor> <!-- CMT --> <interceptor transaction="Container">org.jboss.ejb.plugins.TxInterceptorCMT</interceptor> <interceptor transaction="Container">org.jboss.ejb.plugins.CallValidationIntercep tor</interceptor> <interceptor transaction="Container">org.jboss.ejb.plugins.StatefulSessionInstanc eInterceptor</interceptor> <!-- BMT --> <interceptor transaction="Bean">org.jboss.ejb.plugins.StatefulSessionInstanceInte rceptor</interceptor> <interceptor transaction="Bean">org.jboss.ejb.plugins.TxInterceptorBMT</intercept or> <interceptor transaction="Bean">org.jboss.ejb.plugins.CallValidationInterceptor</ interceptor> <interceptor>org.jboss.resource.connectionmanager.CachedConnectionIn terceptor</interceptor> <interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.StatefulSessionSecurityIntercepto r</interceptor> </container-interceptors>

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 85: Ejb Jsf Struts Flex Jasper

<instance- cache>org.jboss.ejb.plugins.StatefulSessionInstanceCache</instance-cache> <persistence- manager>org.jboss.ejb.plugins.StatefulSessionFilePersistenceManager< /persistence-manager> <container-cache-conf> <cache- policy>org.jboss.ejb.plugins.LRUStatefulContextCachePolicy</cache- policy> <cache-policy-conf> <min-capacity>50</min-capacity> <max-capacity>1000000</max-capacity> <remover-period>1800</remover-period> <max-bean-life>1800</max-bean-life>

<overager-period>300</overager-period> <max-bean-age>600</max-bean-age>

<resizer-period>400</resizer-period> <max-cache-miss-period>60</max-cache-miss-period> <min-cache-miss-period>1</min-cache-miss-period> <cache-load-factor>0.75</cache-load-factor> </cache-policy-conf> </container-cache-conf> <container-pool-conf> <MaximumSize>100</MaximumSize> </container-pool-conf> </container-configuration>

L’élément <MaximumSize> définit le nombre maximum d’instances d’EJB qui sont disponibles dans le pool. Cependant de nouvelles instances peuvent être créées si le nombre de requêtes dépasse la valeur de <MaximumSize>.

L’élément <container­cache­conf> contient un élément <max­bean­age> qui spécifie en secondes la période maximum d’inactivité du bean avant qu’il ne soit passivé.

L’élément <max­bean­life> définit la période maximum en secondes d’inactivité d’un bean avant qu’il ne soit supprimé du pool d’instances passivées.

Il faut redémarrer le serveur après avoir modifié ces paramètres.

Cependant des annotations spécifiques au serveur JBoss existent pour permettre au développeur de redéfinir ces valeurs sans avoir à modifier le fichier de configuration.

Dans le bean PanierCycleBean, la classe a été annotée :

@CacheConfig(removalTimeoutSeconds=15, idleTimeoutSeconds=10 ) public class PanierCycleBean implements PanierCycle, Serializable

removalTimeoutSeconds : temps maximum d’inactivité du bean avant que qu’il ne soit supprimé du cache.

idleTimeoutSeconds : temps maximum d’inactivité du bean avant qu’il ne soit passivé.

La classe ClientStandalone.java du client dans le projet VenteEnLigneClient est la suivante :

package com.eni.dvtejb.client; import java.io.FileInputStream; import java.util.HashMap; import java.util.Properties; import javax.naming.InitialContext; import com.eni.dvtejb.metier.sessions.PanierCycle; // Cette classe fonctionne en dehors du container EJB (JBoss) public class ClientStandalone public static void main(String[] args) throws Exception Properties proprietes = new Properties(); proprietes.load(new FileInputStream("jndi.properties")); InitialContext ctx = new InitialContext(proprietes); PanierCycle panier = (PanierCycle) ctx.lookup("VenteEnLigne/PanierCycleBean/remote"); System.out.println("===> Achat d’un DVD vierge");

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 86: Ejb Jsf Struts Flex Jasper

panier.acheter("DVD", 1); System.out.println("===> Autre achat de DVD"); panier.acheter("DVD", 1); System.out.println("===> Achat d’une souris"); panier.acheter("souris", 1); System.out.println("===> Affichage du contenu du panier:"); HashMap<String, Integer> panierMap = panier.getContenuPanier(); for (String article : panierMap.keySet()) System.out.println(panierMap.get(article) + " " + article); System.out.println("===> Paiement"); panier.payer(); System.out.println("===> Un exception object not found exception est jetée après l’appel de la méthode @Remove payer "); try panier.getContenuPanier(); catch (javax.ejb.NoSuchEJBException e) System.out.println("===> Exception catchée."); System.out.println("===> Création d’une seconde instance de bean pour tester la suppression dû au timeout"); panier = (PanierCycle) ctx.lookup("VenteEnLigne/PanierCycleBean/remote"); System.out.println("===> Achat d’un DVD"); panier.acheter("DVD", 1); System.out.println("===> Pause de 30 secondes. C’est aussi 30 secondes d’inactivité."); Thread.sleep(30 * 1000); try System.out.println("===> Achat d’un autre DVD"); panier.acheter("DVD", 1); catch (javax.ejb.NoSuchEJBException e) System.out.println("===> Le bean a été supprimé");

b. Fichier jndi.properties

Ce fichier contient les propriétés JNDI.

Le conteneur réserve un contexte JNDI pour chaque application. La configuration peut passer par l’utilisation d’une Hashtable, d’une instance de Properties ou d’un fichier jndi.properties qui doit se trouver dans un répértoire du CLASSPATH, ou dans JAVA_HOME/lib.

Il est également possible de coder à l’intérieur du client ces paramètres JNDI.

La classe InitialContext est utilisée conjointement avec le fichier jndi.properties. L’interface Context, implémentée par la classe InitialContext, offre les services nécessaires à la manipulation de l’arborescence. La méthode Object lookup(String name) permet de rechercher une entrée dans le service de nommage.

Contrairement à EJB 2, il n’est pas nécessaire d’utiliser la méthode narrow().

L’arborescence est créée en fonction des déclarations faites dans le descripteur de déploiement web.xml mais aussi en fonction des paramètres et ressources créées et stockées par le serveur au démarrage.

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 87: Ejb Jsf Struts Flex Jasper

java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost:1099

Finalement, exécutez ce client en faisant un clic droit sur la classe ClientStandalone.java > Run as > Java Application.

La consultation des logs du serveur (depuis la console Eclipse) montre que le bean a été passivé après 10 secondes d’inactivité :

17:12:26,726 INFO [STDOUT] ----> Dans la methode postConstructExemple 17:12:26,908 INFO [STDOUT] ----> A implémenter 17:12:26,955 INFO [STDOUT] ----> Dans la methode postConstructExemple 17:12:42,076 INFO [STDOUT] ----> Dans la methode prePassivateExemple

Lors de la passivation du bean, le container EJB écrit l’état conversationnel du bean dans un fichier ou en base. Le bean est sérializé.

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgUwGVJiH82ICwA=-enidentnumber

Page 88: Ejb Jsf Struts Flex Jasper

Appels de Stateless session beans

Nous allons voir dans la suite de ce chapitre quelques­uns des stateless session beans utilisés dans l’application.

1. Cycle de vie

Le cycle de vie des stateless session beans ne possède que 2 états : l’état « n’existe pas » et l’état « prêt ».

Le stateless session bean est créé, les dépendances sont injectées, les méthodes optionnelles callbacks sont appelées et le bean devient prêt. Une unchecked exception entraîne la destruction du bean et la création d’une nouvelle instance du bean. Les stateless session beans ne conservent pas les états entre les différentes requêtes. Deux instances quelconques d’un stateless session bean sont équivalentes. C’est­à­dire qu’un client peut appeler n’importe quelle instance d’un bean présent dans le pool de beans instanciés. Par rapport aux stateful session beans, les stateless session beans offrent d’avantage de scalabilité dans la mesure où ils requièrent moins d’instantiation et sont donc plus adaptés aux besoins d’applications qui ont beaucoup de clients.

Ils conviennent également bien à l’implémentation du design pattern DAO (Data Access Object) qui encapsule les appels à la base.

La scalabilité fait référence de façon générale aux capacités d’adaptation et d’évolution. Dans une application, la scalabilité est la capacité d’une application à pouvoir supporter une charge de connexions et

de traffic. Dans une architecture, la scalabilité est la capacité qu’elle a à évoluer suite à une montée en charge si nécessaire.

Les session beans jouent souvent le rôle de façade. En effet ils constituent une couche abstraite qui permet d’accéder aux entity beans.

L’application nécessite d’implémenter diverses méthodes qui permettent de répondre à des requêtes émanant de la partie cliente. Pour cela, il est utile de créer un stateless session bean qui comporte des méthodes CRUD répondant à la plupart de ces requêtes (création, recherche, lecture, suppression).

Dans le projet VenteEnLigneEJB, créez l’interface FacadeRemote.java dans le package com.eni.dvtejb.metier.sessions :

package com.eni.dvtejb.metier.sessions; public interface FacadeRemote public boolean findByLoginPwd(String login, String pwd);

Créez ensuite la classe bean FacadeBean.java :

package com.eni.dvtejb.metier.sessions; import java.io.Serializable; import javax.ejb.Remote; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext;

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 89: Ejb Jsf Struts Flex Jasper

import javax.persistence.PersistenceContextType; import javax.persistence.Query; @Stateless @Remote (FacadeRemote.class) public class FacadeBean implements FacadeRemote, Serializable private static final long serialVersionUID = 1L; @PersistenceContext(unitName = "VenteEnLigneClientTest", type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; public boolean findByLoginPwd(String login, String pwd) Query query = entityManager.createQuery("SELECT u FROM Utilisateur u WHERE" + " login = ?1 AND password = ?2"); query.setParameter(1, login); query.setParameter(2, pwd); int nombre =query.getResultList().size(); if (nombre == 1 ) return true; else return false;

L’annotation @javax.ejb.Stateless déclare cette classe en tant que stateless session bean. D’autres méthodes peuvent être implémentées dans des stateless session beans. C’est le cas des méthodes de recherche d’articles, produits et catalogues. Le listing suivant montre le code de l’interface distante :

package com.eni.dvtejb.metier.sessions; public interface RechercheRemote public List<Article> rechercheArticles(String nomArticle, BigDecimal prixMinimum, BigDecimal prixMaximum);

La méthode rechercheArticle(String nomArticle, BigDecimal prixMinimum, BigDecimal prixMaximum) permet de faire une recherche par nom d’article, dans une fourchette de prix comprise dans l’intervalle [prixMinimum, prixMaximum].

Le listing suivant montre le code du bean RechercheBean :

package com.eni.dvtejb.metier.sessions; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import javax.ejb.Remote; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.Query; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Article; @Stateless @Remote (RechercheRemote.class) public class RechercheBean implements RechercheRemote, Serializable private static final long serialVersionUID = 1L;

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 90: Ejb Jsf Struts Flex Jasper

private static final Logger log = Logger.getLogger(RechercheBean.class); @PersistenceContext(unitName = "VenteEnLigneModuleEJB", type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; public List<Article> rechercheArticles(String nomArticle, BigDecimal prixMinimum, BigDecimal prixMaximum) log.info("Entrée dans la classe : " + getClass().getName()); log.info("Entrée dans la méthode : rechercheArticles()"); Query query = null; if (nomArticle.equals("Tous")) query = entityManager.createNativeQuery("SELECT a.articleid, a.nom, a.prix FROM article a WHERE " + " a.prix between ?1 and ?2"); query.setParameter(1, prixMinimum); query.setParameter(2, prixMaximum); else query = entityManager.createNativeQuery("SELECT a.articleid, a.nom, a.prix FROM article a WHERE " + " a.nom = ?1 AND a.prix between ?2 and ?3"); query.setParameter(1, nomArticle); query.setParameter(2, prixMinimum); query.setParameter(3, prixMaximum); List articles = query.getResultList(); return articles; // Méthode utile pour l’autocompletion AJAX sous Struts 2 public List rechercheNomArticles() log.info("Entrée dans la classe : " + getClass().getName()); log.info("Entrée dans la méthode : rechercheNomArticles()"); Query query = entityManager.createNativeQuery("SELECT a.nom FROM article a"); List nomArticles = query.getResultList(); return nomArticles;

2. Test de non conservation de l’état

Un stateless session bean ne conserve pas l’état de ses variables. Un client qui appelle une méthode d’un stateless session bean sera servi par une instance de ce stateless session bean lors d’un premier appel. Au deuxième appel de cette méthode, le client sera servi par une nouvelle instance du bean. En revanche, une instance unique d’un stateful session bean sera dédiée au client.

Pour bien montrer cette différence dans la conservation de l’état, voici les codes d’un stateless session bean et d’un stateful session bean qui implémentent une méthode qui incrémente une variable d’instance. Ces session beans sont appelés dans une servlet.

Stateful session bean CompteurStatefulBean.java :

@Stateful @Local(CompteurStatefulLocal.class) public class CompteurStatefulBean

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 91: Ejb Jsf Struts Flex Jasper

private static final Logger log = Logger.getLogger(CompteurStatefulBean.class); private int compteur = 0; public int incrementer() return ++compteur; @PostConstruct public void raz() compteur = 99;

Stateless session bean CompteurStatelessBean.java :

@Stateless @Local(CompteurStatelessLocal.class) public class CompteurStatelessBean private static final Logger log = Logger.getLogger(CompteurStatelessBean.class); private int compteur = 0; public int incrementer() return ++compteur; @PostConstruct public void raz() compteur = 99;

Le client de ces session beans est une servlet :

public class ClientCompteurServlet extends HttpServlet private static final Logger log = Logger.getLogger(ClientCompteurServlet.class); @EJB(name="VenteEnLigne/CompteurStatefulBean/local") private CompteurStatefulLocal compteurStatefulBean; @EJB(name="VenteEnLigne/CompteurStatelessBean/local") private CompteurStatelessLocal compteurStatelessBean; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException int compteurStateful = compteurStatefulBean.incrementer(); int compteurStateless = compteurStatelessBean.incrementer(); response.setContentType("text/html"); PrintWriter output = response.getWriter(); output.println("<CENTER>"); output.println("Méthode appelée : " + request.getMethod() + "<BR>"); output.println("Servlet ClientCompteurServlet exécutée." + "<BR>"); output.println("Le compteur Stateful vaut : " + compteurStateful + "<BR>"); output.println("Le compteur Stateless vaut : " + compteurStateless + "<BR>");

L’exécution répétée de cette servlet dans un même navigateur montre que les deux méthodes raz(), annotées avec

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 92: Ejb Jsf Struts Flex Jasper

l’annotation @PostConstruct, sont exécutées une première fois, lors de l’instantiation du bean. Tant que vous restez sur le même navigateur, la méthode raz() de chaque bean n’est invoquée qu’une seule fois, au tout premier test.

Appel de la servlet depuis un premier navigateur (Chrome) :

Si vous exécutez la servlet dans un autre navigateur, le compteur du stateful session bean s’incrémente à partir de la valeur affichée dans le précédent navigateur. En revanche le compteur du staless session bean reprendra à la valeur 100 car une nouvelle instance a été créée et la méthode raz() du stateless session bean a donc été appelée.

Appel de la servlet depuis un deuxième navigateur (Internet Explorer) :

3. Objets DAO Adresse et Client

Il est courant de mettre en place le design pattern DAO pour les accès à la base. Des DAOs (Data Access Objects), sous la forme de session beans, permettent de mettre à jour des entity beans. Ils sont utilisés pour accéder à la base de données, principalement pour des opérations CRUD (Create, Read, Update, Delete) et permettent ainsi de découpler la couche métier de la couche de données et de cacher l’accès aux entity beans.

L’application requiert au moins deux DAO : un pour l’entité Client et un pour l’entité Adresse. Dans le package com.eni.dvtejb.metier.sessions, créez l’interface session bean ClientDAO.java :

Interface ClientDAO :

package com.eni.dvtejb.metier.sessions; import java.util.List; import com.eni.dvtejb.metier.entities.Client; import javax.ejb.Remote;

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 93: Ejb Jsf Struts Flex Jasper

@Remote public interface ClientDAO public List findAll(); public List findByNom(String nom); public Client findById(Integer id); public void save(Client client); public void merge(Client client); public List findByPrenom(String prenom);

L’implémentation de ce session bean DAO est la suivante :

Session bean ClientDAOImpl :

package com.eni.dvtejb.metier.sessions; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Client; @Stateless public class ClientDAOImpl implements ClientDAO @PersistenceContext private EntityManager em; private Logger log = Logger.getLogger(this.getClass()); public void save(Client client) log.debug("Sauve le client :" + client); em.persist(client); public void merge(Client client) em.merge(client); public List findAll() log.debug("recherche tous les clients"); return em.createQuery("from Client").getResultList(); public Client findById(Integer id) return em.find(Client.class, id); public List findByNom(String nom) log.debug("recherche par nom"); return em.createQuery("from Client c where c.nom = :nom").setParameter("nom", nom).getResultList(); public List findByPrenom(String prenom) log.debug("recherche par prenom"); Query q = em.createNamedQuery("Utilisateur.findByPrenom"); q.setParameter("lePrenom", prenom); List<Client> resultatsNamedQuery = q.getResultList(); return resultatsNamedQuery;

L’entity Adresse requiert un session bean DAO avec les mêmes opérations CRUD que l’entity Client. Voici l’interface

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 94: Ejb Jsf Struts Flex Jasper

AdresseDAO.java :

package com.eni.dvtejb.metier.sessions; import java.util.List; import javax.ejb.Remote; import com.eni.dvtejb.metier.entities.Adresse; @Remote public interface AdresseDAO public List findAll(); public List findByRue(String rue); public Adresse findById(Integer id); public void save(Adresse adresse); public void merge(Adresse adresse); public List findByVille(String ville);

Et son implémentation, le stateless session bean AdresseDAOImpl.java :

package com.eni.dvtejb.metier.sessions; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Client; @Stateless public class AdresseDAOImpl implements AdresseDAO @PersistenceContext private EntityManager em; private Logger log = Logger.getLogger(this.getClass()); public void save(Adresse adresse) log.debug("Sauve l’adresse :" + adresse); em.persist(adresse); public void merge(Adresse adresse) em.merge(adresse); public List findAll() log.debug("recherche toutes les adresses"); return em.createQuery("from Adresse").getResultList(); public Adresse findById(Integer id) return em.find(Adresse.class, id); public List findByRue(String rue) log.debug("recherche par nom"); return em.createQuery("from Adresse a where a.rue = :rue").setParameter("rue", rue).getResultList(); public List findByVille(String ville) log.debug("recherche par ville"); return em.createQuery("from Adresse a where a.ville =

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 95: Ejb Jsf Struts Flex Jasper

:ville").setParameter("ville", ville).getResultList();

4. DAO générique

Dans une architecture à 4 et 5 couches, une séparation peut être faite pour détacher les DAO de la couche métier. Voici le code d’un DAO générique implémenté par un stateless session bean et qui est situé dans la couche domaine :

package com.eni.dvtejb.domaine.sessions; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; /** * Ce DAO session bean générique réside dans la couche Domaine */ @Stateless public class DaoGenerique implements DaoGeneriqueLocal @PersistenceContext EntityManager em; public <T> T ajouter(T t) em.persist(t); em.flush(); em.refresh(t); return t; public void supprimer(Object t) em.merge(t); em.remove(ref); public <T> T chercher(Class<T> type, Object id) return (T) em.find(type, id); public <T> T updater(T t) return (T)em.merge(t); public void flush() em.flush();

Dans la méthode de suppression d’un entity bean, il est nécessaire de faire un merge au préalable car il est détaché. Seuls les entity beans attachés à un contexte de persistance peuvent être supprimés. Il faut donc faire un merge ou une recherche (méthode find()) de l’entity bean au préalable pour le rendre managé, et donc rattaché à un contexte de persistance.

Ce DAO générique réside dans le projet EJB, mais pour une séparation des couches plus nette, il est plus judicieux de placer tous les objets du domaine dans un autre projet EJB. L’interface locale DaoGeneriqueLocale est la suivante :

package com.eni.dvtejb.domaine.sessions; import javax.ejb.Local; @Local public interface DaoGeneriqueLocal <T> T ajouter(T t); void supprimer(Object t); <T> T chercher(Class<T> t, Object id );

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 96: Ejb Jsf Struts Flex Jasper

<T> T updater(T t); //Flush de la session JPA pour MAJ immédiate de la base public void flush();

Dans la couche Services, le DAO générique est injecté dans le stateless session bean client du DAO. Ce client du DAO (DAOClientBean) contient une méthode spécifique. Les méthodes nécessitant des accès à la base utilisent les méthodes du stateless session bean DAO injecté. Voici le code du client, utilisé pour l’entity bean Adresse :

package com.eni.dvtejb.services.sessions; import javax.ejb.Stateless; import javax.ejb.EJB; import com.eni.dvtejb.domaine.sessions.DaoGeneriqueLocal; import com.eni.dvtejb.metier.entities.Adresse; /** * Ce client du DAO session bean DaoGenerique réside dans la couche Services */ @Stateless public class DaoClientBean implements DaoClientLocal @EJB private DaoGeneriqueLocal daoGenerique; public void ajouterAdresse(Adresse adresse) daoGenerique.ajouter(adresse); public void chercherAdresse( Long id) daoGenerique.chercher(Adresse.class, id); @Override public void methodeSpecifique() // TODO Auto-generated method stub

Enfin, voici le code de l’interface locale de ce client, avec son unique méthode métier :

package com.eni.dvtejb.services.sessions; import javax.ejb.Local; @Local public interface DaoClientLocal public void methodeSpecifique();

Le schéma UML suivant illustre l’utilisation du design pattern DAO dans un modèle à 4 couches :

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 97: Ejb Jsf Struts Flex Jasper

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mv6dgwlkH82ICwA=-enidentnumber

Page 98: Ejb Jsf Struts Flex Jasper

Packaging et déploiement de l’application

Dans le développement d’une petite application qui évolue peu et avec une équipe restreinte, les phases de packaging et de déploiement peuvent être totalement gérées dans Eclipse. L’application d’entreprise a été créée via le wizard Eclipse (cf. chapitre Les entity beans et l’API de persistance (JPA)). Par la suite lors de la création d’un nouveau projet (de type EJB ou de type Web) il suffit de mentionner dans le wizard qu’il fait partie du projet EAR pour qu’il soit intégré dans l’archive EAR à déployer.

Le serveur d’application charge ensuite dans la JVM les classes de l’archive déployée, suivant une hiérarchie de classloaders qui, d’une part, isolent l’application contenue dans l’archive des applications contenues dans d’autres archives déployées et, d’autre part, partagent les classes entre les modules déployés.

À l’intérieur de l’archive EAR (Enterprise Archive), le projet EJB est packagé dans un fichier JAR (Java ARchive), les projets Web (Struts 2, JSF 2, Client, Web, FlexGranite) sont packagés dans des fichiers WAR (Web Archive). L’archive EAR contient un fichier au format XML, application.xml, dans le répertoire /META_INF. Ce fichier est le descripteur de déploiement de l’application. Il contient la liste des modules qui doivent au final faire partie de l’archive générée :

Le projet VenteEnLigneConnector est un projet Connector qui n’est pas utilisé dans l’application. En effet, ce type de projet sert à créer des adaptateurs de ressources qui permettent aux plates­formes Java EE d’accéder à des ressources provenant de systèmes d’information d’entreprise (EIS), conformément aux spécifications définies dans la

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlWHH2dkH82ICwA=-enidentnumber

Page 99: Ejb Jsf Struts Flex Jasper

JSR 112 sur l’architecture Connector dans Java EE.

Dans le fichier META­INF/application.xml la déclaration des différents modules est faite à l’intérieur des tags <module>.

La figure suivante illustre le packaging de ces modules dans le fichier EAR :

Le contenu de l’archive déployée VenteEnLigne.ear est visible dans l’écran suivant :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlWHH2dkH82ICwA=-enidentnumber

Page 100: Ejb Jsf Struts Flex Jasper

Les spécifications des EJB insistent sur la séparation des rôles au cours du développement d’une application d’entreprise. Ce n’est pas au développeur des beans de se charger également du packaging et encore moins du déploiement. Ces tâches sont déléguées à l’assembleur d’application et au déployeur. Cette division des tâches, peu souvent mise en pratique sur le terrain, est souhaitable car elle assure une meilleure planification des tâches.

Enfin, il faut noter que dans le développement d’une application plus importante, que ce soit par le nombre de classes ou le nombre de développeurs, et qui est amenée à évoluer en permanence, l’utilisation d’outils comme Ant et Maven est fortement recommandée pour faciliter l’automatisation des différentes phases de développement qui vont de la compilation au déploiement en passant par le packaging.

L’étape du déploiement à travers Eclipse passe par la vue Servers.

Faites un clic droit sur le serveur JBoss et, dans le menu contextuel, ajoutez la ressource VenteEnLigne.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlWHH2dkH82ICwA=-enidentnumber

Page 101: Ejb Jsf Struts Flex Jasper

Puis revenez à la vue Servers et sélectionnez Start pour démarrer le déploiement de l’application. La vue Console permet de vérifier que le déploiement s’est passé correctement.

Le serveur JBoss supporte le déploiement à chaud, c’est­à­dire le redéploiement d’une application sans avoir à redémarrer le serveur. En effet il scanne par défaut le répertoire /deploy toutes les 5 secondes pour vérifier qu’une application a été mise à jour en se basant sur le timestamp de l’archive EAR ou WAR. Si c’est le cas, l’application est redéployée. La période de scanning peut être redéfinit dans le fichier server/xxx/deploy/hdscanner­jboss­beans.xml. Elle correspond à la propriété scanPeriod, exprimée en millisecondes, du bean HDScanner :

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlWHH2dkH82ICwA=-enidentnumber

Page 102: Ejb Jsf Struts Flex Jasper

<!-- Hotdeployment of applications --> <bean name="HDScanner" class="org.jboss.system.server.profileservice.hotdeploy.HDScanner"> <property name="deployer"><inject bean="ProfileServiceDeployer"/></property> <property name="profileService"><inject bean="ProfileService"/></property> <property name="scanPeriod">5000</property>

<property name="scanThreadName">HDScanner</property> </bean>

L’archive EAR est livrée et testée dans des environnements identiques à celui de l’environnement de développement. Cela va de l’environnement de test à l’environnement de production, en passant par les environnements d’intégration et de validation.

La vérification de la non­régression d’une nouvelle version de l’application, après sa livraison, peut être facilitée par la mise en place d’outils d’intégration continue tels que Maven Continumm ou Hudson.

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlWHH2dkH82ICwA=-enidentnumber

Page 103: Ejb Jsf Struts Flex Jasper

JMS : queues et topics

Java Message Service (JMS) est une API utilisée par les MOM (Message­Oriented Middlewares). Les MOM sont des fournisseurs JMS, ils correspondent à un certain type de software qui réside dans les côtés client et serveur d’une architecture et qui supporte les appels asynchrones entre le client et les applications du serveur. Les queues (ou files d’attente) de message fournissent un stockage temporaire quand la destination du programme est occupée ou non connectée.

Les messages ne sont pas envoyés directement à d’autres applications. Ils sont envoyés à des destinations, qui sont soit des queues (modèle point­to­point) soit des topics (modèle publish­subscribe).

Un client JMS qui envoie un message est appelé un producteur et un client JMS qui reçoit un message est appelé un consommateur.

Dans le cas des queues, il existe un et un seul consommateur par message.

Dans le cas des topics, plusieurs consommateurs peuvent récupérer le même message.

Les types de messages possibles sont : TextMessage, StreamMessage, ByteMessage, MapMessage et ObjectMessage.

1. Intérêt de JMS

Les applications qui envoient des messages n’ont pas à s’inquiéter de savoir si les applications réceptrices fonctionnent ou non et inversement, les applications réceptrices n’ont pas à s’inquiéter du statut des applications émettrices. En effet, les applications émettrices et réceptrices interagissent à travers des destinations.

Il est utile d’utiliser JMS dans une application s’il est nécessaire de faire des traitements de façon asynchrone, plutôt que de façon synchrone. C’est par exemple le cas du traitement des commandes comme décrit dans le chapitre suivant.

2. Fournisseurs JMS

Il existe plusieurs fournisseurs JMS Open Source tels qu’ActiveMQ, OpenJMS ou Open MQ (GlassFish). Parmi les payants, on peut citer IBM Websphere MQ (ex MQSeries), SonicMQ, Weblogic. JBoss intègre un fournisseur JMS : HornetQ, anciennement appelé JBoss Messaging. JBoss Messaging était lui­même anciennement appelé JBoss MQ.

Les EJB incluent, depuis la version 2.0, le type MessageDrivenBean qui peut consommer des messages JMS asynchrones.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mta/5ZdkH82ICwA=-enidentnumber

Page 104: Ejb Jsf Struts Flex Jasper

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mta/5ZdkH82ICwA=-enidentnumber

Page 105: Ejb Jsf Struts Flex Jasper

EJB MDB : traitement asynchrone des commandes

Un message­driven bean est un EJB consommateur de message asynchrone. Il peut écouter sur une destination JMS. Cette destination JMS est soit un topic soit une queue. Quand un fournisseur JMS envoie un message vers une destination JMS, le message­driven bean qui écoute traite ce message.

Un message­driven bean n’implémente pas de business interface et est sans état. Concernant les transactions, les seuls attributs autorisés sont REQUIRED et NOT_SUPPORTED, quel que soit le mode transactionnel (à savoir managé par le bean ou managé par le container).

Voici le cycle de vie d’un message­driven bean :

Pour spécifier qu’un bean est un MDB, il faut implémenter l’interface javax.jms.MessageListener et annoter le bean avec l’annotation @MessageDriven.

D’un point de vue fonctionnel, quand le client passe une commande, un mail de confirmation lui est envoyé pour lui signaler que sa commande est en cours de traitement.

D’un point de vue technique, plutôt que d’envoyer un mail directement après la validation de la commande, il est préférable d’envoyer un message dans une destination JMS pour qu’un MDB s’occupe de traiter, de façon périodique, tous les messages en attente et envoyer les mails de façon asynchrone.

Le fichier de configuration des destinations par défaut du serveur est server/default/deploy/messaging/destinations­service.xml. Ces destinations sont déployées quand le serveur démarre. Un topic peut être créé en insérant les lignes suivantes dans ce fichier :

Fichier destinations­services.xml

<mbean code="org.jboss.jms.server.destination.TopicService" name="jboss.messaging.destination:service=Topic,name=MailConfirmatio nMdbTopic" xmbean-dd="xmdesc/Topic-xmbean.xml"> <depends optional-attribute-name="ServerPeer"> jboss.messaging:service=ServerPeer </depends>

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 106: Ejb Jsf Struts Flex Jasper

<depends>jboss.messaging:service=PostOffice</depends> <attribute name="SecurityConfig"> <security> <role name="guest" read="true" write="true"/> <role name="publisher" read="true" write="true" create="false"/> <role name="durpublisher" read="true" write="true" create="true"/> </security> </attribute> </mbean>

Lors du démarrage du serveur, vous pouvez constater au niveau des logs que le topic a bien été démarré par JBoss :

INFO [ConnectionFactory] Connector bisocket://127.0.0.1:4457 has leasing enabled, lease period 10000 milliseconds INFO [ConnectionFactory] org.jboss.jms.server.connectionfactory.ConnectionFactory@c5eeaf started INFO [QueueService] Queue[/queue/ExpiryQueue] started, fullSize=200000, pageSize=2000, downCacheSize=2000

INFO [TopicService] Topic[/topic/MailConfirmationMdbTopic] started,

fullSize=200000, pageSize=2000, downCacheSize=2000

INFO [QueueService] Queue[/queue/DLQ] started, fullSize=200000, pageSize=2000, downCacheSize=2000 INFO [ConnectionFactory] Connector bisocket://127.0.0.1:4457 has leasing enabled, lease period 10000 milliseconds INFO [ConnectionFactory] org.jboss.jms.server.connectionfactory.ConnectionFactory@d82739 started WARN [ConnectionFactoryJNDIMapper] supportsFailover attribute is true on connection factory: jboss.messaging.connectionfactory:service=ClusteredConnectionFactory but post office is non clustered. So connection factory will *not* support failover WARN [ConnectionFactoryJNDIMapper] supportsLoadBalancing attribute is true on connection factory: jboss.messaging.connectionfactory:service=ClusteredConnectionFactory but post office is non clustered. So connection factory will *not* support load balancing INFO [ConnectionFactory] Connector bisocket://127.0.0.1:4457 has leasing enabled, lease period 10000 milliseconds INFO [ConnectionFactory] org.jboss.jms.server.connectionfactory.ConnectionFactory@1ee3c8d started INFO [ConnectionFactoryBindingService] Bound ConnectionManager ’jboss.jca:service=ConnectionFactoryBinding,name=JmsXA’ to JNDI name ’java:JmsXA’

Cette information apparaît également dans la console JMX :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 107: Ejb Jsf Struts Flex Jasper

Ainsi que dans la nouvelle console d’administration de JBoss :

1. Producteur JMS et consommateur JMS

Pour mieux comprendre ce que les Message Driven­Beans peuvent apportés dans une application JMS, il est utile d’écrire dans un premier temps un producteur de message puis un consommateur de message en utilisant l’API JMS exclusivement.

Dans un deuxième temps, un MDB remplacera le client consommateur de message.

Dans le projet VenteEnLigneClient, créez la classe MailConfirmationProducteur.java :

package com.eni.dvtejb.client; import java.io.FileInputStream; import java.util.Properties; import javax.jms.Connection; import javax.jms.ConnectionFactory;

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 108: Ejb Jsf Struts Flex Jasper

import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.naming.InitialContext; public class MailConfirmationProducteur public static void main(String[] args) throws Exception Properties proprietes = new Properties(); proprietes.load(new FileInputStream("jndi.properties")); InitialContext ctx = new InitialContext(proprietes); // 1: recherche d’une connection factory ConnectionFactory factory = (ConnectionFactory) ctx.lookup("ConnectionFactory"); // 2: création d’une connection JMS Connection conn = factory.createConnection(); // 3: création d’une session Session session = conn.createSession(false,Session.AUTO_ACKNOWLEDGE); // 4: Recherche d’une destination Topic topic = (Topic) ctx.lookup("topic/MailConfirmationMdbTopic"); // 5: création d’un producteur de message MessageProducer producteur = session.createProducer(topic); // 6: publication d’un message TextMessage msg = session.createTextMessage(); msg.setText("Mail de confirmation pour le client."); producteur.send(msg); producteur.close(); System.out.println("Message envoyé.");

Cette classe représente le producteur de message. Les étapes classiques pour la création d’un message sont décrites dans le code de la classe. Il faut maintenant créer une classe qui va consommer ce message. C’est le but de la classe MailConfirmationConsommateur.java :

package com.eni.dvtejb.client; import java.io.FileInputStream; import java.util.Properties; import javax.annotation.PreDestroy; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.naming.InitialContext; public class MailConfirmationConsommateur implements MessageListener public static void main(String[] args) throws Exception new MailConfirmationConsommateur(); public MailConfirmationConsommateur() throws Exception

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 109: Ejb Jsf Struts Flex Jasper

Properties proprietes = new Properties(); proprietes.load(new FileInputStream("jndi.properties")); InitialContext ctx = new InitialContext(proprietes); // 1: recherche d’une connection factory ConnectionFactory factory = (ConnectionFactory) ctx.lookup("ConnectionFactory"); // 2: création d’une connection JMS Connection conn = factory.createConnection(); // 3: création d’une session Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); // 4. Recherche d’une destination Topic topic = (Topic) ctx.lookup("topic/MailConfirmationMdbTopic"); // 5: création d’un consommateur de message MessageConsumer consommateur = session.createConsumer(topic); consommateur.setMessageListener(this); System.out.println("Client JMS MailConfirmationConsommateur à l’écoute de messages."); conn.start(); public void onMessage(Message msg)

if (msg instanceof TextMessage) TextMessage tm = (TextMessage) msg; // L’envoi d’un mail de confirmation au client est ici simulé par l’affichage d’un message au niveau des logs. try String mail = tm.getText(); System.out.println("Le client JMS MailConfirmationConsommateur a reçu le message : " + mail); catch (JMSException e) e.printStackTrace(); @PreDestroy public void remove() System.out.println("Suppression du client JMS MailConfirmationConsommateur.");

Lancez le client consommateur puis le client producteur. La consultation des logs montre que le message a été produit puis consommé :

Producteur :

Message envoyé.

Consommateur :

Client JMS MailConfirmationConsommateur à l’écoute de messages. Le client JMS MailConfirmationConsommateur a reçu le message : Mail de confirmation pour le client.

D’autres consommateurs peuvent être à l’écoute et consommez ce message puisque la destination est décrite sous forme de topic. Cela n’aurait pas été possible si la destination avait été une queue (file d’attente), car dans ce cas,

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 110: Ejb Jsf Struts Flex Jasper

chaque message est consommé par un seul consommateur.

2. Producteur JMS et consommateur MDB

Les EJB offrent la possibilité de consommer un message JMS, tel que celui produit par le producteur JMS précédent, à l’aide des EJB MDB. Dans la pratique, le producteur JMS est appelé à partir d’un session bean. L’EJB MDB suivant consomme les messages de type TextMessage qui sont produits par le producteur JMS précédent ainsi que les messages de type ObjectMessage qui sont produits par le stateful session bean PanierBean, comme illustré dans le schéma qui suit.

Pour définir un EJB MDB, créez le package com.eni.dvtejb.metier.mdb dans le projet VenteEnLigneEJB. Créez ensuite la classe du message driven­bean MailConfirmationMdbBean.java :

package com.eni.dvtejb.metier.mdb; import java.io.UnsupportedEncodingException; import java.util.Properties; import java.util.Set; import javax.annotation.PreDestroy; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven;

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 111: Ejb Jsf Struts Flex Jasper

import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import javax.jms.TextMessage; import javax.mail.MessagingException; import javax.mail.Transport; import javax.mail.internet.MimeMessage; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.entities.Lignecommande; @MessageDriven(mappedName = "topic/MailConfirmationMdbTopic", activationConfig = @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/MailConfirmationMdbTopic") ) public class MailConfirmationMdbBean implements MessageListener

private static final Logger log = Logger.getLogger(MailConfirmationMdbBean.class); public MailConfirmationMdbBean() log.info("Initialisation de l’envoi du mail depuis MailConfirmationMdbBean"); public void onMessage(Message message)

// Pour la classe de test MailConfirmationProducteur if (message instanceof TextMessage) TextMessage mail = (TextMessage) message; // L’envoi d’un mail de confirmation au client est ici simulé par l’affichage d’un message au niveau des logs. try String leMail = mail.getText(); log.info(" Envoi du mail : " + leMail); log.info(" ---------------------------------------------- ----- "); log.info(" ---------------------------------------------- ----- "); log.info(" Mail envoyé."); catch (JMSException e) e.printStackTrace(); else if (message instanceof ObjectMessage) ObjectMessage lemessage = (ObjectMessage) message; try Commande commande = (Commande)lemessage.getObject(); Client client = commande.getUtilisateurFk(); Adresse adresse = client.getAdresseFk(); String contenuMail = "Bonjour " + client.getNom() + " " + client.getPrenom() + ". \n" + "Votre numéro de commande est : " + commande.getCommandeid() + " \n" + "Vous avez commandé les articles suivants

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 112: Ejb Jsf Struts Flex Jasper

: " + " \n" ; String lesArticles = ""; Set <Lignecommande> listeArticles = commande.getLignecommandeCollection(); for (Lignecommande lc : listeArticles) Article article = lc.getArticleFk(); lesArticles += article.getNom() + " : " + article.getPrix() + " euros. \n" ; contenuMail += lesArticles; String ladresse = " \n" + "Votre adresse est : "+ " \n" + adresse.getNumero() + " rue " + adresse.getRue() + " " + adresse.getCodepostal() + " " + adresse.getVille(); contenuMail += ladresse; contenuMail += "\n Votre commande est en cours de traitement."; log.info(" Envoi du mail au client: " ); log.info(" ---------------------------------------- ----------- "); sendMsg(client.getEmail(), "Confirmation de votre commande.", contenuMail); log.info(" ---------------------------------------- ----------- "); log.info(" Mail envoyé au client."); catch (MessagingException e) e.printStackTrace(); catch (NamingException e) e.printStackTrace(); catch (JMSException e) e.printStackTrace(); catch (UnsupportedEncodingException e) e.printStackTrace(); protected void sendMsg(String email, String subject, String

body) throws MessagingException, NamingException, UnsupportedEncodingException Properties props = new Properties(); InitialContext ictx = new InitialContext(props); javax.mail.Session mailSession = (javax.mail.Session) ictx.lookup("java:/Mail"); MimeMessage message = new MimeMessage(mailSession); message.setSubject(subject); message.setRecipients(javax.mail.Message.RecipientType.TO, javax.mail.internet.InternetAddress.parse(email, false)); message.setText(body); message.saveChanges(); Transport transport = mailSession.getTransport("smtp"); try transport.connect(); transport.sendMessage(message, message.getAllRecipients()); log.info("Message envoyé");

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 113: Ejb Jsf Struts Flex Jasper

finally transport.close(); @PreDestroy public void remove() log.info("Suppression de MailConfirmationMdbBean.");

Un MDB doit avoir un constructeur public sans argument. Depuis la version 3 des EJB, un message driven­bean n’est pas obligé d’implémenter l’interface javax.ejb.MessageDrivenBean. Les MDBs doivent implémenter la méthode onMessage() de l’interface javax.jms.MessageListener. Cette méthode contient la logique métier qui doit être exécutée à la réception d’un message.

L’annotation @MessageDriven possède un attribut activationConfig qui permet de configurer les propriétés avec une autre annotation : @ActivationConfigProperty dont les attributs sont propertyName et propertyValue.

De plus, les destinations sur lesquelles des MDBs écoutent et qui n’existent pas sont créées au moment du déploiement.

Lancez de nouveau le producteur. La consultation des logs permet de constater que le message est maintenant également consommé par le MDB :

INFO [ServerSessionEndpoint] Received send for ID:JBM-a177a336-07f8- 4c0f-bb86-4b26a2e1e367 reliable: true INFO [ServerSessionEndpoint] Done send INFO [STDOUT] Initialisation de l’envoi du mail depuis MailConfirmationMdbBean WARN [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container WARN [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container INFO [STDOUT] MailConfirmationMdbBean - envoi du mail : Mail de confirmation pour le client. INFO [STDOUT] --------------------------------------------------- INFO [STDOUT] Mail ENVOYE INFO [STDOUT] ---------------------------------------------------

Le stateful session bean PanierBean appelle la méthode producteurMail(Commande commande) dès que la commande est persistée en base.

Elle récupère la ConnectionFactory et le topic qui sont injectés par le container, grâce à l’annotation @Resource. Le message envoyé est de type ObjectMessage de manière à pouvoir récupérer le contenu de la commande et afficher les détails dans le mail de confirmation de commande au client. La création et l’envoi d’un message dans la méthode producteurMail(Commande commande) reprend des étapes qui ont été utilisées dans le producteur MailConfirmationProducteur (création d’une connexion JMS, création d’un producteur, publication du message sur la destination de type Topic).

@Resource(mappedName ="ConnectionFactory") private ConnectionFactory connectionFactory; @Resource(mappedName="topic/MailConfirmationMdbTopic") private Topic leTopic; private void producteurMail(Commande commande) log.info("Debut de la méthode producteurMail"); Connection conn = null; Session session = null; try

Producteur MailConfirmationProducteur

Producteur PanierBean

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 114: Ejb Jsf Struts Flex Jasper

conn = connectionFactory.createConnection(); session = conn.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer producteur = session.createProducer(leTopic); ObjectMessage message = session.createObjectMessage(); message.setObject(commande); producteur.send(message); catch (JMSException e) e.printStackTrace(); finally try log.info("Fin de la méthode producteurMail"); session.close(); catch (JMSException e) e.printStackTrace();

JBoss possède une implémentation propre de l’API JavaMail. Les deux librairies utilisées se trouvent dans <serverJBoss>/common/lib/mail.jar (package javax.mail) et dans <serverJBoss>/common/lib/mail­plugin.jar (package org.jboss.mail). La configuration du service de mail est faite dans le fichier <serverJBoss>/server/default/deploy/mail­service.xml. Dans l’exemple, une adresse Gmail est utilisée :

<mbean code="org.jboss.mail.MailService" name="jboss:service=Mail"> <attribute name="JNDIName">java:/Mail</attribute> <attribute name="User">eniejb3</attribute> <attribute name="Password">xxxxxxx</attribute> <attribute name="Configuration"> <configuration> <!-- Change to your mail server prototocol --> <property name="mail.store.protocol" value="pop3"/> <property name="mail.transport.protocol" value="smtp"/> <!-- Change to the user who will receive mail --> <property name="mail.user" value="eniejb3"/> <!-- Change to the mail server --> <property name="mail.pop3.host" value="pop3.gmail.com"/> <!-- Change to the SMTP gateway server --> <property name="mail.smtp.host" value="smtp.gmail.com"/> <!-- The mail server port --> <property name="mail.smtp.port" value="587"/> <!-- Change to the address mail will be from --> <property name="mail.from" value="[email protected]"/> <property name="mail.smtp.auth" value="true"/> <property name="mail.smtp.starttls.enable" value="true"/> <!-- Enable debugging output from the javamail classes --> <property name="mail.debug" value="true"/> </configuration> </attribute> <depends>jboss:service=Naming</depends> </mbean>

Le mail qui est envoyé au client reprend la liste des articles faisant partie de la commande, son adresse, son nom et son prénom, à partir de l’entité Commande qui fait partie du message JMS.

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 115: Ejb Jsf Struts Flex Jasper

Sous Windows, assurez­vous de ne pas être derrière un firewall. Pour vérifier que vous avez accès aux serveurs SMTP de Gmail, vous pouvez utiliser la commande telnet pour tester la connexion sur les ports 587

et 465. En cas de connexion impossible, les messages d’erreur suivants s’affichent :

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjboIqFkH82ICwA=-enidentnumber

Page 116: Ejb Jsf Struts Flex Jasper

Quartz et EJB MDB : traitement des cartes de crédits

Nous pouvons imaginer le traitement suivant :

Les dates d’expiration de certaines cartes de crédit sont dépassées. Un traitement batch s’occupe de parcourir toutes les commandes dans la table COMMANDE et analyse les dates d’expirations des cartes de crédit associées aux commandes.

Elle envoie un mail d’avertissement au client si sa carte de crédit est déjà expirée ou si elle expire dans un mois. Cet email est simulé par un message dans la console.

Ce traitement intervient tous les jours, à 4h du matin.

Quartz est un framework Open Source de planification (également appelé scheduler) de tâches qui est intégré à JBoss. Il constitue une très bonne combinaison avec les Message Driven­Beans pour déclencher des évènements à des horaires précis et à des fréquences précises. C’est une bonne alternative aux EJB Timer Service (vus au chapitre Les EJB Timers).

Dans le projet VenteEnLigneEJB, créez l’interface CommandeDAO et le stateless session bean CommandeDAOImpl.

L’interface CommandeDAO.java

package com.eni.dvtejb.metier.sessions; import javax.ejb.Remote; @Remote public interface CommandeDAO void traitementCarteCredit();

Le stateless session bean CommandeDAOImpl

package com.eni.dvtejb.metier.sessions; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.ejb.Stateless; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; @Stateless public class CommandeDAOImpl implements CommandeDAO @PersistenceContext private EntityManager em; @Override public void traitementCarteCredit() Date aujourdhui = new Date(); long t = aujourdhui.getTime(); java.sql.Date aujourdhuiSQL = new java.sql.Date(t); List commandes = em.createQuery("from Commande").getResultList();

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mk2a2rFkH82ICwA=-enidentnumber

Page 117: Ejb Jsf Struts Flex Jasper

Iterator iter = commandes.iterator(); while (iter.hasNext()) Commande commande = (Commande) iter.next(); if (commande.getDateExpirationCartecredit().before(aujourdhuiSQL)) // Appel d’un autre Session Bean depuis un Session Bean try InitialContext initialContext = new InitialContext(); ClientDAO clientDAO = (ClientDAO) initialContext.lookup("VenteEnLigne/ClientDAOImpl/remote"); Client client = clientDAO.findById(commande.getUtilisateurFk().getUtilisateurid()); System.out.println("************************ CARTE DE CREDIT EXPIREE ! ***************************************"); System.out.println("******* Envoi d’email d’avertissement au client :" + client.getNom() + " " + client.getPrenom()); System.out.println("******* Numéro de carte de credit: " + commande.getNumeroCartecredit()); System.out.println("******* Date d’expiration : " + commande.getDateExpirationCartecredit()); System.out.println("************************ CARTE DE CREDIT EXPIREE ! ***************************************"); catch (NamingException e) // Fin If // Fin boucle while

L’annotation @org.jboss.annotation.ejb.ResourceAdapter permet de spécifier au MDB d’être à l’écoute d’autres évènements, comme Quartz.

JBoss inclut le Resource Adapter quartz­ra.rar qui permet à des évènements gérés par Quartz de notifier un Message Driven­Bean. Au lieu d’écouter une file JMS, le MDB est à l’écoute d’un job Quartz. Dans ce cas, la classe TraitementCarteCreditMdbBean n’implémente pas l’interface javax.jms.MessageListener mais l’interface org.quartz.Job :

package com.eni.dvtejb.metier.mdb; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.ejb3.annotation.ResourceAdapter; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import com.eni.dvtejb.metier.sessions.CommandeDAO; @MessageDriven(activationConfig = // Utilisation du scheduler quartz embarqué dans JBoss //@ActivationConfigProperty(propertyName = "cronTrigger", propertyValue = "0 0 4 * * ?")) // Message émis toutes les 5 secondes toutes les 10 secondes : 0/10 @ActivationConfigProperty(propertyName = "cronTrigger", propertyValue = "0/10 * * * * ?")) // Le Resource Adapter quartz-ra.rar est dans le répertoire server/default/deploy @ResourceAdapter("quartz-ra.rar") public class TraitementCarteCreditMdbBean implements Job

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mk2a2rFkH82ICwA=-enidentnumber

Page 118: Ejb Jsf Struts Flex Jasper

public TraitementCarteCreditMdbBean() System.out.println(" ---------------- Initialisation de l’envoi du mail depuis TraitementCarteCreditMdbBean ---------------- "); public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException try InitialContext initialContext = new InitialContext(); CommandeDAO commandeDAO = (CommandeDAO) initialContext.lookup("VenteEnLigne/CommandeDAOImpl/remote"); commandeDAO.traitementCarteCredit(); catch (NamingException e) // TODO Auto-generated catch block e.printStackTrace(); // System.out.println("---------------- ------------- --- ENVOI DE MAIL ---------------- ---------------- ");

La propriété cronTrigger utilise des expressions cron qui permettent de mettre en place des fréquences de déclenchement d’évènements (tâches). L’interface Job oblige à implémenter la méthode execute() qui doit contenir le traitement à exécuter.

Dans le cas où une tâche ne doit être appelée qu’une fois à un horaire spécifique, vous pouvez implémenter l’interface org.quartz.StatefulJob.

Après avoir démarré le serveur et déployé l’application, le scheduler est en marche. Si vous paramétrez la fréquence de traitement à 10 secondes par exemple, vous constatez que le message d’envoi de mails s’affiche, pour les clients dont la date d’expiration de carte de crédit est dépassée.

Scheduler avec une fréquence de 10 secondes :

@ActivationConfigProperty(propertyName = "cronTrigger", propertyValue = "0/10 * * * * ?"))

Affichage d’un message dans les logs, toutes les 10 secondes :

14:29:10,041 INFO [STDOUT] ************************ CARTE DE CREDIT EXPIREE ! *************************************** 14:29:10,041 INFO [STDOUT] ******* Envoi d’email d’avertissement au client :Fernandes Celinio 14:29:10,041 INFO [STDOUT] ******* Numéro de carte de credit: 4716718496946025 14:29:10,041 INFO [STDOUT] ******* Date d’expiration : 2008-12-01 14:29:10,041 INFO [STDOUT] ************************ CARTE DE CREDIT EXPIREE ! ***************************************

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mk2a2rFkH82ICwA=-enidentnumber

Page 119: Ejb Jsf Struts Flex Jasper

Présentation de SOAP et WSDL

1. SOAP

Les Web Services sont des composants qui communiquent via le protocole SOAP. Le protocole SOAP (Simple Object Access Protocol) définit un standard basé sur XML pour l’échange d’informations structurées et typées entre des applications disparates, en passant en général par le protocole HTTP. Ainsi, la request et la response sont des messages SOAP. Le corps d’un message est défini à l’intérieur d’une enveloppe qui est décrite à l’intérieur de la balise <soap:enveloppe>. La grammaire de ce message est définie par WSDL.

La structure d’un message SOAP est la suivante :

<xml version="1.0"> <soap:envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingstyle="http://www.w3.org/2001/12/soap-encoding"> <soap:header> ... </soap:header> <soap:body> ... <soap:fault> ... </soap:fault> </soap:body> </soap:envelope> </xml>

2. WSDL

Le fichier WSDL (Web Services Description Language) décrit les informations contenues dans un Web Service, où il se trouve et comment y accéder. Typiquement, la structure d’un fichier WSDL est la suivante :

<wsdl:definitions> <wsdl:types> ... </wsdl:types> <wsdl:message> ... </wsdl:message> <wsdl:porttype> <wsdl:operation> <wsdl:input> ... </wsdl:input> <wsdl:output> ... </wsdl:output> </wsdl:operation> </wsdl:porttype> <wsdl:binding> <wsdl:operation> <wsdl:input>... </wsdl:input> <wsdl:output>... </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service> <wsdl:port>... </wsdl:port> </wsdl:service> </wsdl:definitions>

La balise <wsdl:message> définit les paramètres d’entrée et de sortie des méthodes. La balise <wsdl:porttype> décrit les interfaces qui sont exposées par le Web Service. Les styles du fichier WSDL peuvent être de type Remote Procedure Call (RPC/encoded, RPC/literal) ou Document (Document/encoded, Document/literal, Document/literal wrapped). Ces styles affectent la structure du message SOAP qui peut être plus ou moins concis et verbeux en balises XML. Le Web Service est donc mis à disposition via son fichier WSDL qui peut être récupéré de plusieurs manières : à partir d’une URL, via une recherche sur un annuaire de Web Services UDDI (Universal Description, Discovery and Integration)…

Pour résumer, SOAP et WSDL travaillent conjointement : SOAP s’occupe du transport des données entre le Web Service et l’application cliente et WSDL s’occupe de fournir des descriptions détaillées du Web Service.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mpb4cr1kH82ICwA=-enidentnumber

Page 120: Ejb Jsf Struts Flex Jasper

Exposer des EJB 3 en tant que services Endpoint

JAX­WS (Java API for XML Web Services) est l’API standard (JSR 224) dans Java EE 5 pour développer des Web Services. L’API JAXB (Java Architecture for XML Binding) fournit la sérialisation Java/XML nécessaire pour définir le mapping entre WSDL et Java. Dans les EJB 3, les services endpoints sont des stateless session beans qui sont annotés avec l’annotation @javax.jws.Webservice. Les références des Web Services sont ensuite injectées dans les clients à l’aide de l’annotation @javax.xml.ws.WebServiceRef.

Pour déclarer une classe (que ce soit un simple POJO dans un container web ou un stateless session bean dans un container EJB) en tant que Web Service, il suffit donc de l’annoter avec l’annotation @javax.jws.WebService de l’API JAX­WS. Cependant exposer un stateless session bean en tant que Web Service au lieu d’exposer un simple POJO permet de faire bénéficier le Web Service des avantages du container EJB, qui sont listés dans le chapitre Introduction.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mtn448RkH82ICwA=-enidentnumber

Page 121: Ejb Jsf Struts Flex Jasper

Web Service de vérification du numéro de carte de crédit avec JAX­WS

Dans l’application, le développement et l’utilisation de Web Services peuvent être pertinents à plusieurs endroits. Par exemple, un Web Service chargé de vérifier la disponibilité des articles. Il peut être utilisé à deux endroits : lors de l’affichage des articles et lors de la validation de la commande. Il interroge la table STOCK pour récupérer la quantité d’articles restante et utilise un stateless session bean qui interroge un entity bean avec une requête nommée. Un autre exemple, qui sera développé dans ce chapitre, est un Web Service de validation du compte en banque mais pour des raisons de simplicité l’application se contente de vérifier le numéro de carte de crédit, en utilisant l’algorithme de Luhn.

À cause de versions différentes et incompatibles entre celles fournies par le JDK 1.6 (pile WS, saadj) et celles qui viennent avec JBoss 6, il est nécessaire de faire la manipulation suivante :

Dans le répertoire du Java Runtime installé, K:\ENI\DeveloppementEJB3\jre1.6.0_07\lib\, créez le répertoire endorsed et copiez les jars suivants :

K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\lib\endorsed\resolver.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\lib\endorsed\serializer.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\lib\endorsed\xalan.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\lib\endorsed\xercesImpl.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\client\jbossws­native­jaxrpc.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\client\jbossws­native­jaxws.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\client\jbossws­native­jaxws­ext.jar

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\client\jbossws­native­saaj.jar

Le répertoire endorsed est utilisé pour spécifier les librairies jar que l’on veut charger avant les autres.

1. Les classes du Web Service

Le but est d’exposer un EJB de vérification de carte de crédit, de type stateless session bean, en tant que Web Service pour pouvoir l’utiliser par la suite à travers le web. Créez le package com.eni.dvtejb.metier.services.

Il est possible de créer, au choix, soit une seule classe bean soit une interface et une classe bean qui l’implémente. Dans le cas du dernier choix, la première chose à faire est de créer une remote interface :

package com.eni.dvtejb.metier.services; import java.rmi.Remote; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style;

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 122: Ejb Jsf Struts Flex Jasper

@WebService

@SOAPBinding(style = Style.RPC, use=Use.LITERAL) public interface VerifierCarteCredit extends Remote public boolean validerCarteCredit(String typeCarteCredit, String numeroCartecredit);

Implémentez ensuite cette interface dans un stateless session bean :

package com.eni.dvtejb.metier.services; import javax.ejb.Remote; import javax.ejb.Stateless; import javax.jws.WebMethod; import javax.jws.WebService; /* * Cette classe est un bean endpoint */ @Stateless

@WebService(endpointInterface

="com.eni.dvtejb.metier.services.VerifierCarteCredit") @Remote(VerifierCarteCredit.class) public class VerifierCarteCreditBean public VerifierCarteCreditBean() @WebMethod(operationName="verifCarteCredit") public boolean validerCarteCredit(String typeCarteCredit, String numeroCarteCredit) if ("VISA".equals(typeCarteCredit)) if ((numeroCarteCredit.length() != 13 && numeroCarteCredit.length() != 16) || Integer.parseInt(numeroCarteCredit.substring(0, 1)) != 4) return false; else if ("MASTERCARD".equals(typeCarteCredit)) if (numeroCarteCredit.length() != 16 || Integer.parseInt(numeroCarteCredit.substring(0, 2)) < 51 || Integer.parseInt(numeroCarteCredit.substring(0, 2)) > 55) return false; else if ("AMEX".equals(typeCarteCredit)) if (numeroCarteCredit.length() != 15 || (Integer.parseInt(numeroCarteCredit.substring(0, 2)) != 34 && Integer.parseInt(numeroCarteCredit.substring(0, 2)) != 37)) return validerLuhn(numeroCarteCredit); // Tous les numéros de carte de crédit doivent être validés par l’algorithme de Luhn private boolean validerLuhn(String numeroString) char[] charTableau = numeroString.toCharArray(); int[] numero = new int[charTableau.length]; int total = 0; for (int i=0; i < charTableau.length; i++)

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 123: Ejb Jsf Struts Flex Jasper

numero[i] = Character.getNumericValue(charTableau[i]); for (int i = numero.length-2; i > -1; i-=2) numero[i] *= 2; if (numero[i] > 9) numero[i] -= 9; for (int i=0; i < numero.length; i++) total += numero[i]; if (total % 10 != 0) return false; return true;

Il est possible d’ajouter les annotations JAX­WS dans le bean et dans l’interface. L’annotation @WebService expose cette classe en tant que Web Service. En annotant également l’interface, toutes les méthodes publiques sont exposées en tant qu’opérations des Web Services, même si elles ne sont pas annotées avec l’annotation @WebMethod dans la classe bean. S’il n’y a aucune méthode annotée avec l’annotation @WebMethod alors toutes les méthodes du bean sont exposées en tant qu’opérations du Web Service. L’annotation sur la méthode validerCarteCredit(String typeCarteCredit, String numeroCarteCredit) est donc ici optionelle.

Même si JAX­WS permet de déclarer dans des SEI des méthodes qui ne sont pas exposées, c’est une bonne pratique de les définir dans les beans uniquement. C’est le cas de la méthode du calcul de l’algorithme de

Luhn validerLuhn (String numeroString).

L’annotation @SOAPBinding(style = Style.RPC, use=Use.LITERAL) définit le style du Web Service (l’autre style possible est DOCUMENT) et le style du message SOAP qui peut être LITERAL ou ENCODED.

La propriété OperationName de l’annotation @WebMethod permet de redéfinir les valeurs générées par défaut par JAX­WS dans le fichier WSDL. Le nom de l’opération par défaut est le nom de la méthode Java. Le nom de l’opération devient donc verifCarteCredit, au lieu de validerCarteCredit :

@WebMethod(operationName="verifCarteCredit") public boolean validerCarteCredit(String typeCarteCredit, String numeroCarteCredit)

Le fait de fournir une interface SEI (Service Endpoint Interface) impose de déclarer cette interface dans le bean à l’aide de la propriété endPointInterface de l’annotation @WebService.

L’utilisation des annotations du cycle de vie du session bean (@PostConstruct et @PreDestroy) est possible.

La prochaine étape passe par la création du fichier WSDL. C’est une étape simple puisque pendant la phase de déploiement le serveur JBoss détecte un Web Service dans l’archive et génère automatiquement un fichier WSDL, comme le montre les traces suivantes :

12:54:03,657 INFO [DefaultEndpointRegistry] register: jboss.ws:context=VenteEnLigne- VenteEnLigneEJB,endpoint=VerifierCarteCreditBean 12:54:05,314 INFO [WSDLFilePublisher] WSDL published to: file:/K:/ENI/DeveloppementEJB3/jboss- 6.0.0.M1/server/default/data/wsdl/VenteEnLigne.ear/VenteEnLigneEJB.j ar/VerifierCarteCreditBeanService62018.wsdl

Le fichier WSDL généré est visible à l’adresse :

http://127.0.0.1:8085/VenteEnLigne­VenteEnLigneEJB/VerifierCarteCreditBean?wsdl

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 124: Ejb Jsf Struts Flex Jasper

Dans JBoss les Web Services déployés avec succès sont visibles dans l’interface de JBossWS Service Endpoint Manager qui est accessible à l’adresse suivante : http://localhost:8085/jbossws/services. JBossWS est le framework Web Services faisant partie de JBoss AS. Il implémente la spécification JAX­WS.

2. Les clients du Web Service

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 125: Ejb Jsf Struts Flex Jasper

L’étape suivante est la création d’un client du Web Service. Trois types de client sont mis en œuvre : un client JAX­RPC, un client JAX­WS et un client Eclipse.

a. Client JAX­RPC

Ce client peut être de type JAX­RPC (Java API for XML­based Remote Procedure Calls) standalone et donc être lancé en dehors du container :

package com.eni.dvtejb.client; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.rpc.Service; import javax.xml.rpc.ServiceFactory; import com.eni.dvtejb.metier.services.VerifierCarteCredit; public class ServiceClientStandaloneRPC public static void main(String[] args) throws Exception URL url = new URL("http://127.0.0.1:8085/VenteEnLigne- VenteEnLigneEJB/VerifierCarteCreditBean?wsdl"); QName qname = new QName("http://services.metier.dvtejb.eni.com/", "VerifierCarteCreditBeanService"); ServiceFactory factory = ServiceFactory.newInstance(); Service remote = factory.createService(url, qname); VerifierCarteCredit service = (VerifierCarteCredit) remote.getPort(VerifierCarteCredit.class); boolean isValide = false ; String numCC = "4716718496946025"; isValide = service.validerCarteCredit( "VISA", numCC); if (isValide) System.out.println("Le numero de carte de credit " + numCC + " est valide"); else System.out.println("Le numero de carte de credit " + numCC + " est invalide");

Les packages javax.xml.QName et javax.xml.rpc contiennent les classes et interfaces nécessaires pour le développement d’un client.

b. Client JAX­WS

La servlet cliente de type JAX­WS du Web Service est la suivante :

package com.eni.dvtejb.web; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.ws.WebServiceRef; import com.eni.dvtejb.metier.services.VerifierCarteCredit;

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 126: Ejb Jsf Struts Flex Jasper

public class TestServiceServlet extends javax.servlet.http.HttpServlet private static final long serialVersionUID = 1L; @WebServiceRef(wsdlLocation="http://127.0.0.1:8085/VenteEnLigne- VenteEnLigneEJB/VerifierCarteCreditBean?wsdl") static VerifierCarteCredit service; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException boolean isValide = false ; String numCC = "4716718496946025"; sValide = service.validerCarteCredit( "VISA", numCC); response.setContentType("text/html"); PrintWriter output = response.getWriter(); output.println("<CENTER>"); output.println("Méthode appelée : " + request.getMethod() + "<BR>"); output.println("Servlet TestServiceServlet exécutée." + "<BR>"); if (isValide) output.println("Le numero de carte de credit " + numCC + " est valide"); else output.println("Le numero de carte de credit " + numCC + " est invalide"); output.println("</CENTER>");

L’annotation @WebServiceRef permet de déclarer une référence à un Web Service en passant par l’injection de dépendance. Elle indique l’endroit où se trouve le fichier WSDL.

Les attributs possibles de cette annotation sont listés dans le tableau suivant :

c. Client Eclipse

Vous pouvez aussi profiter des nombreuses fonctionnalités d’Eclipse pour tester ce Web Service en utilisant l’explorateur de Web Services.

Pour cela, cliquez sur Run puis Launch the Web Services Explorer. Sélectionnez ensuite l’icône WSDL dans le menu du haut à droite pour ouvrir la page WSDL. Puis dans l’explorateur (Navigator) cliquez sur WSDL Main et entrez une URL dans la boîte de dialogue Open WSDL. Puis cliquez sur Go et rentrez les paramètres du Web Service. Cliquez sur Go pour exécuter le Web Service. Le résultat de l’exécution de l’opération WSDL s’affiche dans la fenêtre du bas, comme le montre la capture d’écran suivante :

Attributs de @WebServiceRef

Description

name Définit le nom JNDI dans l’espace de nommage.

mappedName Nom global JNDI. Spécifique à chaque serveur d’application.

type Le type Java du service.

value La classe Java du service.

wsdlLocation L’endroit où se trouve le fichier WSDL.

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 127: Ejb Jsf Struts Flex Jasper

Une liste de Web Services publiques est disponible à l’adresse suivante : http://www.xmethods.net

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MtyPCtpkH82ICwA=-enidentnumber

Page 128: Ejb Jsf Struts Flex Jasper

JAX­RS : Les RESTful Web Services

REST est un acronyme pour REpresentational State Transfer et qui trouve ses origines dans une thèse de Roy Fielding. C’est un modèle d’architectures orienté ressources, ce qui veut dire que ce sont des ressources (identifiées par des URLs) qui vont être exposées, et non des méthodes comme c’est le cas avec SOAP. Les méthodes des URLs peuvent être GET, POST, PUT, DELETE, HEAD, OPTIONS. Le principe de REST est de permettre l’accès à des services en publiant des « ressources ». Les « ressources » sont de simples classes Java avec des annotations JAX­RS. Ces annotations décrivent le chemin de la ressource (l’URL utilisée pour y accéder), la méthode HTTP utilisée pour appeler une certaine méthode (pour lecture, création…) et le type MIME qui est retourné/accepté par une méthode. Les méthodes HTTP sont associées à un verbe.

L’API JAX­RS correspond à la JSR 311 qui a été intégrée dans la spécification Java EE 6. Son but est de mettre à disposition une API qui permette d’écrire et d’exposer des Web Services de type RESTful, autrement dit des Web Services sans état. L’API JAX­RS peut être utilisée dans de simples POJOs mais aussi dans des stateless session bean comme c’est le cas dans ce chapitre. L’implémentation choisie de JAX­RS est Jersey mais il existe d’autres implémentations telles que Apache CXF, RESTlet, RESTEasy de JBoss… Jersey 1.1.4.1 implémente JAX­RS 1.1 et est l’implémentation de référence de Sun.

L’injection de dépendance (par exemple @PersistenceContext) ne fonctionne pas pour les ressources JSR­311 dans Java EE 5 (et donc les EJB 3.0). Dans le cas d’un stateful session bean, Jersey instancie le bean comme un

simple POJO. En revanche elle fonctionne dans les EJB 3.1 (Java EE 6).

1. Installation de Jersey

Récupérez les librairies Jersey sous forme de bundle à l’adresse suivante :

http://download.java.net/maven/2/com/sun/jersey/jersey­archive/1.1.4.1/jersey­archive­1.1.4.1.zip

Dézippez cette archive et ajoutez les librairies suivantes dans le Build Path du projet EJB :

K:\ENI\DeveloppementEJB3\jersey­archive­1.1.4.1\lib\asm­3.1.jar

K:\ENI\DeveloppementEJB3\jersey­archive­1.1.4.1\lib\jersey­core­1.1.4.1.jar

K:\ENI\DeveloppementEJB3\jersey­archive­1.1.4.1\lib\jersey­server­1.1.4.1.jar

K:\ENI\DeveloppementEJB3\jersey­archive­1.1.4.1\lib\jsr311­api­1.1.1.jar

Copiez­les également dans le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\lib

Modifiez le fichier web.xml du projet VenteEnLigneWeb pour y déclarer la servlet ServletContainer et pour que Jersey n’intercepte que les requêtes du type /REST/*

<servlet> <servlet-name>RESTJersey</servlet-name> <servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>RESTJersey</servlet-name> <url-pattern>/REST/*</url-pattern> </servlet-mapping>

La dernière chose à faire est d’ajouter le module EJB en tant que librairie du projet Web. Dans Eclipse, allez dans les propriétés du projet VenteEnLigneWeb puis dans Java EE Module Dependencies :

Méthode HTTP Verbe

POST Create (Créer)

GET Read (Lire)

PUT Update (Mettre à jour)

DELETE Delete (Supprimer)

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 129: Ejb Jsf Struts Flex Jasper

2. Méthodes HTTP dans un Stateless Session bean et leurs clients

Le diagramme suivant montre un client HTTP qui envoie des requêtes http au Web Service. Le Web Service est déployé dans le serveur web JBoss. Les ressources sont traitées par le framework Jersey.

Un Web Service peut être développé pour interroger la base de données pour ramener une liste des noms des utilisateurs. Le framework JAXB (Java Architecture for XML Binding) est utilisé pour transformer cette liste au format XML. On parle de marshalling.

Dans REST, les méthodes HTTP les plus utilisées sont les suivantes :

GET : pour obtenir la représentation d’une ressource.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 130: Ejb Jsf Struts Flex Jasper

POST : pour créer la représentation d’une ressource.

PUT : pour mettre à jour ou créer la représentation d’une ressource.

DELETE : pour supprimer la représentation d’une ressource.

Dans le projet VenteEnLigneEJB, créez l’interface ListeUtilisateursRESTRemote :

package com.eni.dvtejb.metier.services; import javax.ejb.Remote; @Remote public interface ListeUtilisateursRESTRemote ListeNomsUtilisateurs ListerUtilisateurs();

Puis définissez le stateless session bean ListeUtilisateursRESTBean qui implémente cette interface :

package com.eni.dvtejb.metier.services; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import org.jboss.logging.Logger; @Path("/AfficherListeUtilisateurs") @Produces("application/xml", "application/json") @Stateless public class ListeUtilisateursRESTBean implements ListeUtilisateursRESTRemote private static final Logger log = Logger.getLogger(ListeUtilisateursRESTBean.class); private EntityManagerFactory emFactory; private EntityManager em; @PostConstruct public void creerEntityManager() emFactory = Persistence.createEntityManagerFactory("VenteEnLigneModuleEJB"); em = emFactory.createEntityManager(); @PreDestroy public void fermerEntityManager() em.close(); @GET public ListeNomsUtilisateurs ListerUtilisateurs() log.info("Debut méthode ListerUtilisateurs()"); Query query = em.createNativeQuery("Select u.nom from Utilisateur u"); List<String> nomsutilisateurs = query.getResultList(); ListeNomsUtilisateurs liste = new ListeNomsUtilisateurs(); for (String u : nomsutilisateurs) liste.nomsUtilisateurs.add(u); log.info("Fin méthode ListerUtilisateurs()"); return liste;

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 131: Ejb Jsf Struts Flex Jasper

L’annotation @Path indique que cette ressource sera hébergée à l’URL dont le chemin est /AfficherListeUtilisateurs.

L’annotation @GET spécifie que la méthode ListerUtilisateurs() traitera les requêtes HTTP de type GET.

Ce service retourne des données dans les formats application/xml et application/json. JSON (JavaScript Object Notation) est un format léger d’échange de données. Les types MIME de la réponse HTTP sont spécifiés avec l’annotation @Produces. L’annotation @Consumes peut être utilisée pour spécifier les représentations en entrée des ressources. Ce Web Service ne possède qu’une seule méthode mais dans le cas de plusieurs méthodes, la méthode par défaut du Web Service est celle qui ne possède pas l’annotation @Path.

Le type de retour de la méthode ListerUtilisateurs() est ListeNomsUtilisateurs. La classe ListeNomsUtilisateurs est un bean JAXB annoté avec l’annotation @XmlRootElement. JAX­RS supporte les beans JAXB et détecte que le Content­Type est application/xml et que la classe ListeNomsUtilisateurs a une annotation JAXB. Du coup JAXB sera utilisé pour écrire l’objet ListeNomsUtilisateurs dans le flux de sortie http. Ce modèle de programmation se rapproche du design pattern DTO (Data Transfer Object). La classe ListeNomsUtilisateurs, qui joue en fait le rôle de DTO, est annotée avec des annotations JAXB :

package com.eni.dvtejb.metier.services; import java.util.ArrayList; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class ListeNomsUtilisateurs XmlElement protected java.util.List<String> nomsUtilisateurs; public ListeNomsUtilisateurs() nomsUtilisateurs = new ArrayList<String>(0);

Pour tester ce Web Service, invoquez la requête HTTP de type GET dans un navigateur à l’URL http://localhost:8085/VenteEnLigneWeb/REST/AfficherListeUtilisateurs. Vous verrez la réponse suivante :

Au démarrage, Jersey parcourt les classes de l’application et détermine si elles sont des ressources.

16:46:51,107 INFO [ClasspathResourceConfig] Scanning for root resource and provider classes in the paths: K:\ENI\DeveloppementEJB3\jboss- 6.0.0.M1\server\default\tmp\4sl4r6n-q2prtb-g334qhfq-1-g334saf6- cj\VenteEnLigneWeb.war\WEB-INF\lib K:\ENI\DeveloppementEJB3\jboss- 6.0.0.M1\server\default\tmp\4sl4r6n-q2prtb-g334qhfq-1-g334saf6- cj\VenteEnLigneWeb.war\WEB-INF\classes 16:46:51,138 INFO [ClasspathResourceConfig] Root resource classes found: class com.eni.dvtejb.metier.services.VerifierCarteCreditRESTBean 16:46:51,138 INFO [ClasspathResourceConfig] Provider classes found: 16:46:52,896 INFO [WebApplicationImpl] Initiating Jersey application, version ’Jersey: 1.1.4.1 11/24/2009 01:30 AM’

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 132: Ejb Jsf Struts Flex Jasper

Le Web Service précédent peut être complété en ajoutant une méthode qui retourne le prénom d’un utilisateur à partir de son nom. Les requêtes avec paramètres sont gérées à l’aide de l’annotation javax.ws.rs.QueryParam :

@GET @Path("/annuaire") @Produces("text/html") public String getPrenomUtilisateur(@DefaultValue("Bock") @QueryParam("nomUtilisateur") String nom) log.info("Debut méthode getPrenomUtilisateur(String nom)"); Query query = em.createNativeQuery("Select u.prenom from Utilisateur u where u.nom = ?1"); query.setParameter(1, nom); String prenom = (String)query.getSingleResult(); log.info("Fin méthode getPrenomUtilisateur(String nom)"); String retour = "Le prenom de l’utilisateur est : " + prenom; return retour;

L’URL http://localhost:8085/VenteEnLigneWeb/REST/AfficherListeUtilisateurs/annuaire?nomUtilisateur=Fernandes invoque la méthode getPrenomUtilisateur(String nom). Le chemin relatif de l’URL est la concaténation du PATH de la classe et du PATH de la méthode. Si aucun paramètre n’est inclus dans l’URL, la recherche du prénom se fera sur la valeur par défaut du paramètre qui est spécifiée avec l’annotation @DefaultValue.

a. Outil Curl

L’outil Open Source Curl permet d’envoyer, en lignes de commande DOS, des requêtes au serveur en utilisant le protocole HTTP. Il permet d’envoyer du contenu à travers n’importe quelle méthode HTTP. Vous pouvez le télécharger sur le site officiel : http://curl.haxx.se

Les deux requêtes précédentes peuvent ainsi être exécutées avec les commandes suivantes dans une fenêtre DOS :

curl "http://localhost:8085/VenteEnLigneWeb/REST/AfficherListeUtilisateurs" curl "http://localhost:8085/VenteEnLigneWeb/REST/AfficherListeUtil isateurs/annuaire"

La méthode POST du protocole HTTP permet de mettre à jour des données. Les paramètres d’une requête sont mappés avec les paramètres d’une méthode en les préfixant avec l’annotation @javax.ws.rs.QueryParam, avec la possibilité de définir des valeurs par défaut grâce à l’annotation @javax.ws.rs.DefaultValue. Ces annotations sont fournis par l’API JAX­RS.

Pour pouvoir gérer le mode transactionnel, il est nécessaire d’ajouter l’annotation @TransactionManagement(javax.ejb.TransactionManagementType.BEAN) au niveau de la classe car l’unité de persistance est de type JTA donc le container gère les transactions par défaut. L’EntityManager est créé à l’intérieur d’une transaction JTA active, de sorte qu’il fera automatiquement partie de la transaction JTA et commitera/rollbackera avec la transaction JTA.

/* * Méthode avec transactions. */ @POST @Path("MAJ") @Produces("text/html") public Response changeNumeroRue( @QueryParam("adId")

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 133: Ejb Jsf Struts Flex Jasper

@DefaultValue("0") Long adresseId, @QueryParam("numRue") @DefaultValue("22") BigDecimal numeroRue) log.info("Debut méthode changeNumeroRue(long adresseId, BigDecimal numeroRue) avec POST"); try EntityManagerFactory emFactory1; EntityManager em1; UserTransaction utx1 = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction"); log.info("Avant begin"); utx1.begin(); emFactory1 = Persistence.createEntityManagerFactory("VenteEnLigneModuleEJB"); em1 = emFactory1.createEntityManager(); Adresse adresse = em1.find(Adresse.class,adresseId); adresse.setNumero(numeroRue); em1.persist(adresse); log.info("Apres persist"); em1.close(); utx1.commit(); catch (Exception e) e.printStackTrace(); log.info("Fin méthode changeNumeroRue(long adresseId, BigDecimal numeroRue)"); Response response = Response.status(200).build(); //update return response;

Le retour de la méthode est une réponse de type javax.ws.rs.core.Response avec un code 200 (OK) pour indiquer que la requête a été exécutée avec succès.

Le client de ce Web Service peut être un simple fichier HTML avec un lien vers l’URL de la ressource, qui est appelée avec un bouton à l’intérieur d’un tag <form> afin de spécifier le nom de la méthode HTTP à POST :

<html> <body> <form id="form1" action="http://localhost:8085/VenteEnLigneWeb/REST/AfficherLi steUtilisateurs/MAJ?adId=0&numRue=33" method="POST"> <input type="submit" value="Lanceur"> </form> </body> </html>

Les requêtes vues dans les services précédents sont des requêtes statiques mais JAX­RS permet également de créer des URLs dynamiques. Par exemple, la méthode suivante permet à l’utilisateur d’ajouter le paramètre de son choix à l’URL. Les annotations @javax.ws.rs.Path(« /nomArt ») et @ javax.ws.rs.PathParam(« nomArt ») permettent de passer un paramètre à la méthode et de filtrer les articles pour ramener le prix de l’article choisi. Le dernier paramètre de l’URI est assigné au paramètre nomArticle de la méthode :

@POST @Path("/nomArt") @Produces("text/html") public String recupererPrixArticle ( @PathParam("nomArt") @DefaultValue("Un") String nomArticle) log.info("Debut méthode recupererPrixArticle POST"); if (null == em)

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 134: Ejb Jsf Struts Flex Jasper

log.info("em est nul."); Query query = em.createNativeQuery("Select a.prix from Article a where a.nom = ?1"); query.setParameter(1, nomArticle); BigDecimal prix = (BigDecimal)query.getSingleResult(); log.info("Fin méthode getPrenomUtilisateur(String nom)"); String retour = "Le prix de l’article est : " + prix; return retour;

La méthode HTTP PUT est associée à des requêtes de mise à jour. Elle peut par exemple servir dans un Web Service de mise à jour du mot de passe.

@PUT @Path("MAJPWD/idU/pwd") @Produces("text/html") public Response updaterPwd( @PathParam("idU") Long idUtilisateur, @PathParam("pwd")String pwd) log.info("Debut méthode updatePassword POST"); try EntityManagerFactory emFactory2; EntityManager em2; UserTransaction utx2 = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction"); utx2.begin(); emFactory2 = Persistence.createEntityManagerFactory("VenteEnLigneModuleEJB"); em2 = emFactory2.createEntityManager(); Utilisateur u = em2.find(Utilisateur.class, idUtilisateur); u.setPassword(pwd); em2.persist(u); em2.close(); utx2.commit(); catch (Exception e) e.printStackTrace(); log.info("Fin méthode updatePassword "); Response response = Response.status(200).build(); //update return response;

La plupart des navigateurs Web ne peuvent générer de requêtes PUT ou DELETE. Ils peuvent uniquement générer des requêtes GET et POST. De même, le tag HTML <FORM> ne supporte que les requêtes GET et POST (jusqu’à la version HTML 4). Pour tester qu’un Web Service gère correctement des requêtes PUT, il faut un client capable de générer des requêtes PUT. Plusieurs solutions existent, comme par exemple l’utilisation d’AJAX dans un fichier HTML.

b. AJAX / XMLHttpRequest

L’objet XMLHttpRequest est capable d’appeler le serveur et de capturer la réponse. Il peut émettre des requêtes HTTP avec la méthode send(). Le fichier http://localhost:8085/VenteEnLigneWeb/ClientWebServiceREST.html contient le code JavaScript utilisant la méthode send() pour tester le RESTful Web Service développé sur le stateless session bean :

<script language="javascript" type="text/javascript"> function getNewHTTPObject() var xmlhttp;

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 135: Ejb Jsf Struts Flex Jasper

/** Special IE only code ... */ /*@cc_on @if (@_jscript_version >= 5) try xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); catch (e) try xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); catch (E) xmlhttp = false; @else xmlhttp = false; @end @*/ /** Every other browser on the planet */ if (!xmlhttp && typeof XMLHttpRequest != ’undefined’) try xmlhttp = new XMLHttpRequest(); catch (e) xmlhttp = false; return xmlhttp; var xmlHttp = getNewHTTPObject(); function chargeURLPut(url) var mimeType = "text/plain"; xmlHttp.open(’PUT’, url, true); // true : asynchrone false: synchrone xmlHttp.setRequestHeader(’Content-Type’, mimeType); xmlHttp.send(null); </script>

L’appel au Web Service peut se faire ensuite au travers d’un évènement onClick déclenché dans un bouton :

<form method="POST" name="ajax" action=""> <input type="button" value="Lanceur PUT" onClick="chargeURLPut(’http://localhost:8085/VenteEnLig neWeb/REST/AfficherListeUtilisateurs/MAJPWD/4/tutu’)"> </form>

Le test avec Ajax doit obligatoirement s’exécuter à l’intérieur de la webapp, c’est­à­dire dans le même domaine et le même port que la webapp dans laquelle tourne le Web Service. La raison étant qu’Ajax

n’autorise pas, pour des raisons de sécurité, l’émission d’une requête (qui renvoie une réponse) vers un domaine et un port qui sont différents de ceux à partir duquel l’émission est faite. Cette restriction est connue sous le nom de « Same­origin policy ». Ceci limite les attaques de type cross­site scripting.

c. Curl

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 136: Ejb Jsf Struts Flex Jasper

L’outil Curl, mentionné précédemment, permet également de lancer des requêtes de type PUT.

curl -v --request PUT "http://localhost:8085/VenteEnLigneWeb/REST/AfficherListeUtilisateu rs/MAJPWD/4/zdldre"

La vérification en base, dans la table UTILISATEUR, permet de s’assurer que le Web Service a bien mis à jour le mot de passe (zdldre) de l’utilisateur dont l’id est 4.

d. Plugin Poster pour Firefox

Sous Firefox, il existe un plugin, Poster, qui permet d’interagir avec les Web Services pour émettre des requêtes HTTP, définir leur contenu et leur type. Ce plugin est disponible à l’URL suivante : https://addons.mozilla.org/en­US/firefox/addon/2691. Entrez l’URL de la requête à tester, choisissez l’action PUT et cliquez sur le bouton Submit :

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 137: Ejb Jsf Struts Flex Jasper

La réponse (Status 200 OK) s’affiche dans un nouvel écran :

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 138: Ejb Jsf Struts Flex Jasper

Vous pouvez également exécuter la requête qui liste les utilisateurs. Entrez l’URL http://localhost:8085/VenteEnLigneWeb/REST/AfficherListeUtilisateurs puis définissez le type du contenu de la réponse à application/xml. Pour cela allez dans l’onglet Headers, entrez Accept dans le champ Name et application/xml dans le champ Value. Cliquez ensuite sur le bouton Add/change.

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 139: Ejb Jsf Struts Flex Jasper

Le code HTTP de la réponse retournée est 200 et la réponse est au format XML comme le montre l’écran suivant :

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 140: Ejb Jsf Struts Flex Jasper

Le tableau suivant liste les quatre URIs mappées aux ressources REST qui ont été développées :

URI Ressource REST (Jersey) Méthode HTTP

http://localhost:8085/VenteEnLigne Web/REST/AfficherListeUtilisateurs

ListeUtilisateursRESTBean.java Méthode ListerUtilisateurs

GET

http://localhost:8085/VenteEnLigne Web/REST/AfficherListeUtilisateurs/ annuaire?nomUtilisateur=<Parametre>

ListeUtilisateursRESTBean.java Méthode getPrenomUtilisateur

GET

http://localhost:8085/VenteEnLigne Web/REST/AfficherListeUtilisateurs /MAJ?adId=0&numRue=143

ListeUtilisateursRESTBean.java Méthode changeNumeroRue

POST

http://localhost:8085/VenteEnLigne Web/REST/AfficherListeUtilisateurs /Ballon

ListeUtilisateursRESTBean.java Méthode recupererPrixArticle

POST

http://localhost:8085/VenteEnLigne Web/REST/AfficherListeUtilisateurs /MAJPWD/4/toto

ListeUtilisateursRESTBean.java Méthode updatePwd

PUT

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsroV/FkH82ICwA=-enidentnumber

Page 141: Ejb Jsf Struts Flex Jasper

Mise en place d’une alerte avec un stateless session bean

Pour illustrer avec un exemple l’utilisation du service Timer, on peut considérer le scénario suivant : implémenter la possibilité d’interroger toutes les heures en base les quantités disponibles en stock des produits et d’envoyer un mail d’alerte au fournisseur quand le nombre en stock (colonne QUANTITE de la table STOCK) d’un produit est inférieur à 5000 afin de réalimenter les stocks.

1. Les interfaces TimerService et Timer

L’écriture d’un EJB Timer Service est essentiellement basée sur une API composée des interfaces javax.ejb.TimerService et javax.ejb.Timer.

a. Création du timer

L’interface javax.ejb.TimerService contient quatre méthodes createTimer qui permettent de créer des timers qui expirent à des dates précises ou à intervalles réguliers.

Timers qui ne se déclenchent qu’une seule fois :

Timer createTimer (Date expiration, Serializable info) : expire à une date précise ;

Timer createTimer (long durée, Serializable info) : expire après une certaine durée.

Timers qui se déclenchent de façon récurrente :

Timer createTimer (Date expirationInitiale, long DureeIntervalle, Serializable info) : expire une première fois à une date précise puis à intervalles réguliers ;

Timer createTimer (long dureeInitiale, long DureeIntervalle, Serializable info) : expire une première fois après une certaine durée puis à intervalles réguliers.

b. Récupération des timers

L’interface javax.ejb.TimerService contient également une méthode getTimers() pour récupérer une Collection de tous les timers associés au bean.

Des évènements, associés aux timers, peuvent donc être déclenchés à des dates précises ou à intervalles réguliers.

L’objet TimerService peut être récupéré par injection grâce à l’annotation @javax.annotation.Resource, en passant par l’interface SessionContext avec la méthode getTimerService() ou bien encore via un lookup JNDI.

En utilisant l’injection, lorsque le stateless session bean est créé, le champ ctx sera initialisé avec le bon SessionContext.

private @Resource SessionContext ctx;

L’interface javax.ejb.Timer fournit des méthodes pour récupérer des informations sur les timers : l’information sur l’objet sérializé, la date de la prochaine expiration du timer ainsi que le temps restant.

c. Evènements associés aux timers

On peut fournir une méthode qui est déclenchée par le service Timer au moment où le timer expire avec l’annotation @Timeout.

C’est dans la méthode annotée avec l’annotation @Timeout que le traitement ponctuel ou répétitif doit être inséré. L’annotation @Timeout spécifie que la méthode doit être exécutée quand le timer arrive à échéance.

Une méthode timeout doit retourner void et prendre un unique paramètre de type javax.ejb.Timer. Elle ne peut lancer d’exception.

d. Arrêt des timers

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 142: Ejb Jsf Struts Flex Jasper

La méthode cancel() de l’interface javax.ejb.Timer offre la possibilité d’arrêter un timer.

2. La gestion des stocks

Dans le projet VenteEnLigneEJB, créez l’interface distante et le stateless session bean associé au timer.

Le code de l’interface distante StockTimerServiceRemote.java est le suivant :

package com.eni.dvtejb.metier.sessions; import javax.ejb.Remote; @Remote public interface StockTimerServiceRemote public void verifierStocks(); public void arreterTimers();

Le stateless session bean StockTimerService utilise le TimerService. L’annotation @Timeout sur la méthode maintenance(javax.ejb.Timer timer) est une alternative plus élégante et plus rapide à la façon de développer des EJB Timers dans la version 2.1 des EJB. Elle consistait à implémenter l’interface javax.ejb.TimedObject et la méthode ejbTimeout().

La méthode verifierStock() est la méthode qui démarre le timer.

StockTimerService.java

package com.eni.dvtejb.metier.sessions; import java.util.Iterator; import java.util.List; import javax.annotation.Resource; import javax.ejb.Stateless; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Stock; @Stateless public class StockTimerService implements StockTimerServiceRemote private static final Logger log = Logger.getLogger(StockTimerService.class); // @Resource // private SessionContext sessionCtx; //Une référence à une instance de TimerService est injectée. @Resource javax.ejb.TimerService timerService; @PersistenceContext(unitName="VenteEnLigneModuleEJB") private EntityManager em; private Timer timer = null; public static final String SELECT_ARTICLE = "Select a FROM Article a join a.stockFK stock where stock.stockid = ?1"; public StockTimerService()

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 143: Ejb Jsf Struts Flex Jasper

public void verifierStocks() // On récupère une instance timerService via l’interface SessionContext si elle n’a pas été injectée //timerService = sessionCtx.getTimerService(); // Création d’un timer // Syntaxe : timerService.createTimer(dateDebut en millisecondes, intervalle en millisecondes, instanceSerializable); // Le timer se déclenchera pour la 1ère fois au bout de 5000 millisecondes, soit 5 secondes, // à partir du moment où la méthode est invoquée. // Puis il partira en timeout toutes les 30000 millisecondes, soit 30 secondes timer = timerService.createTimer(5000, 30000, "TimerStocks"); // La méthode est surchargée : // 1) createTimer(long duree, Serializable objet) ==> le timer expire au bout de "duree" secondes, // à partir du moment où la méthode est appelée. // 2) createTimer(Date uneDate, Serializable objet) //timerService.createTimer(new Date(System.currentTimeMillis() + 1000), Serializable objet); // Cette méthode est invoquée par le container quand l’objet Timer a expiré. @Timeout public void maintenance(javax.ejb.Timer timer) log.info("------------ Méthode maintenance() : debut -- ------"); String intoTimer = (String) timer.getInfo(); log.info(intoTimer); List stocks = em.createQuery("from Stock").getResultList(); Iterator iter = stocks.iterator(); while (iter.hasNext()) Stock stock = (Stock) iter.next(); if (stock.getQuantite().intValue() < 5000) Query query = em.createQuery(SELECT_ARTICLE); query.setParameter(1, stock.getStockid()); List<Article> resultat = query.getResultList(); String nomArticle = null; for (Article a : resultat) nomArticle = a.getNom(); log.info("*********** STOCK EPUISE OU PRESQUE ! ****************"); log.info("****** Envoi d’email d’avertissement au fournisseur : ") ; log.info("******* Nom de l’article : " + nomArticle); log.info("******* Stockid : " + stock.getStockid()); log.info("******* Quantite : " + stock.getQuantite()); log.info("*********** STOCK EPUISE OU PRESQUE ! ****************"); public void arreterTimers() log.info("------ Méthode arreterTimer() : debut ------"); // Récupération des timers associés au bean et affichage des informations for (Object obj : timerService.getTimers()) Timer timer = (Timer)obj; log.info("-- Informations sur le timer :-- " + timer.getInfo());

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 144: Ejb Jsf Struts Flex Jasper

log.info("-- Prochain timeout : -- " + timer.getNextTimeout()); log.info("-- Temps restant : -- " + timer.getTimeRemaining()); if ("TimerStocks".equals((String)timer.getInfo())) log.info(" ---------- Arrêt du timer TimerStocks --------- " ); timer.cancel(); log.info("------ Méthode arreterTimer() : fin ------");

Pour tester ce timer, il faut naturellement écrire un client qui appelle la méthode qui déclenche le timer. Dans le projet VenteEnLigneWeb, créez la servlet TestTimer.java :

TestTimer.java

package com.eni.dvtejb.web; import java.io.IOException; import java.io.PrintWriter; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.sessions.StockTimerServiceRemote; public class TestTimer extends javax.servlet.http.HttpServlet private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(TestTimer.class); private static String DEMARRER = "demarrer"; private static String STOPPER = "stopper"; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException try log.info(" ***** Debut TestTimer.doGet **** "); InitialContext initialContext = new InitialContext(); StockTimerServiceRemote stockTimerServiceRemote = (StockTimerServiceRemote) initialContext.lookup("VenteEnLigne/StockTimerService/remote"); String appel = request.getParameter("appel"); if (DEMARRER.equals(appel)) stockTimerServiceRemote.verifierStocks(); else if (STOPPER.equals(appel)) stockTimerServiceRemote.arreterTimers(); response.setContentType("text/html"); PrintWriter output = response.getWriter(); output.println("<CENTER>"); output.println("Méthode appelée : " + request.getMethod() + "<BR>"); output.println("Servlet TestTimer exécutée." + "<BR>"); output.println("Valeur du paramètre d’appel : " +

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 145: Ejb Jsf Struts Flex Jasper

request.getParameter("appel")); output.println("</CENTER>"); log.info(" ********** Fin TestTimer.doGet ************ "); catch (NamingException e) e.printStackTrace();

Dans JBoss, pour que la gestion de plusieurs ressources à commiter en une phase (non­XA) puisse se faire, il est nécessaire de modifier le fichier <serveur Jboss>/server/default/conf/jbossts.properties en ajoutant la ligne suivante dans la section des propriétés JTA (name= « JTA ») :

<properties depends="arjuna" name="jta"> ... <property name="com.arjuna.ats.jta.allowMultipleLastResources" value="true" /> </properties>

Modifiez le fichier de configuration web.xml pour déclarer et mapper cette servlet en ajoutant le code suivant :

<!-- Test du timer --> <servlet> <description></description> <display-name>TestTimer</display-name> <servlet-name>TestTimer</servlet-name> <servlet-class>com.eni.dvtejb.web.TestTimer</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestTimer</servlet-name> <url-pattern>/TestTimer</url-pattern> </servlet-mapping>

Enfin modifiez le fichier index.jsp pour y ajouter l’appel à la méthode de création du timer et l’appel à la méthode d’arrêt du timer, par le biais d’un paramètre dans la requête d’appel de la servlet.

<br> <h2>Invocation de TestTimer Service (démarrage)</h2> <a href="TestTimer?appel=demarrer">Cliquez ici pour appeler le timer TimerStocks</a> <br> <h2>Invocation de TestTimer Service (arrêt)</h2> <a href="TestTimer?appel=stopper">Cliquez ici pour arrêter le timer TimerStocks</a> <br>

Invoquez l’appel du client pour démarrer le timer. L’URL d’appel est http://localhost:8085/VenteEnLigneWeb/TestTimer?appel=demarrer, comme mentionnée dans la copie d’écran suivante :

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 146: Ejb Jsf Struts Flex Jasper

La consultation des logs permet de constater que le timer est bien déclenché. Le service Timer commence le décompte lorsque la méthode createTimer() est invoquée. Cinq secondes se sont écoulées lors du premier timeout, puis le timer expire toutes les 30 secondes et par conséquent la méthode annotée avec @Timeout est appelée toutes les 30 secondes, comme le montre l’extrait des logs suivant :

15:15:39,841 INFO [TestTimer] ** Debut TestTimer.doGet ** 15:15:39,849 INFO [TestTimer] ** Fin TestTimer.doGet ** 15:15:44,851 INFO [StockTimerService] ------------ Méthode maintenance() : debut -------- 15:15:44,851 INFO [StockTimerService] TimerStocks 15:15:44,853 INFO [STDOUT] Hibernate: select stock0_.stockid as stockid46_, stock0_.quantite as quantite46_ from Stock stock0_ 15:15:44,861 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:15:44,864 INFO [STDOUT] Hibernate: select produit0_.produitid as produitid40_1_, produit0_.CATALOGUE_FK as CATALOGUE4_40_1_, produit0_.description as descript2_40_1_, produit0_.nom as nom40_1_, catalogue1_.catalogueid as catalogu1_47_0_, catalogue1_.description as descript2_47_0_, catalogue1_.nom as nom47_0_ from Produit produit0_ left outer join Catalogue catalogue1_ on produit0_.CATALOGUE_FK=catalogue1_.catalogueid where produit0_.produitid=? 15:15:44,866 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,866 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:15:44,866 INFO [StockTimerService] ******* Nom de l’article : Chaussures 15:15:44,866 INFO [StockTimerService] ******* Stockid : 0 15:15:44,867 INFO [StockTimerService] ******* Quantite : 1000 15:15:44,867 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,868 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:15:44,871 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,871 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:15:44,871 INFO [StockTimerService] ******* Nom de l’article : Ballon 15:15:44,871 INFO [StockTimerService] ******* Stockid : 1 15:15:44,871 INFO [StockTimerService] ******* Quantite : 2000 15:15:44,871 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,873 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_,

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 147: Ejb Jsf Struts Flex Jasper

article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:15:44,876 INFO [STDOUT] Hibernate: select produit0_.produitid as produitid40_1_, produit0_.CATALOGUE_FK as CATALOGUE4_40_1_, produit0_.description as descript2_40_1_, produit0_.nom as nom40_1_, catalogue1_.catalogueid as catalogu1_47_0_, catalogue1_.description as descript2_47_0_, catalogue1_.nom as nom47_0_ from Produit produit0_ left outer join Catalogue catalogue1_ on produit0_.CATALOGUE_FK=catalogue1_.catalogueid where produit0_.produitid=? 15:15:44,878 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,879 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:15:44,879 INFO [StockTimerService] ******* Nom de l’article : Tablette chocolat noir 15:15:44,879 INFO [StockTimerService] ******* Stockid : 2 15:15:44,879 INFO [StockTimerService] ******* Quantite : 3000 15:15:44,879 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,881 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:15:44,883 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:15:44,883 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:15:44,883 INFO [StockTimerService] ******* Nom de l’article : Tablette chocolat blanc 15:15:44,884 INFO [StockTimerService] ******* Stockid : 3 15:15:44,884 INFO [StockTimerService] ******* Quantite : 4000 15:15:44,884 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,847 INFO [StockTimerService] ------------ Méthode maintenance() : debut -------- 15:16:14,847 INFO [StockTimerService] TimerStocks 15:16:14,848 INFO [STDOUT] Hibernate: select stock0_.stockid as stockid46_, stock0_.quantite as quantite46_ from Stock stock0_ 15:16:14,855 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:16:14,858 INFO [STDOUT] Hibernate: select produit0_.produitid as produitid40_1_, produit0_.CATALOGUE_FK as CATALOGUE4_40_1_, produit0_.description as descript2_40_1_, produit0_.nom as nom40_1_, catalogue1_.catalogueid as catalogu1_47_0_, catalogue1_.description as descript2_47_0_, catalogue1_.nom as nom47_0_ from Produit produit0_ left outer join Catalogue catalogue1_ on produit0_.CATALOGUE_FK=catalogue1_.catalogueid where produit0_.produitid=? 15:16:14,861 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,861 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:16:14,861 INFO [StockTimerService] ******* Nom de l’article : Chaussures 15:16:14,861 INFO [StockTimerService] ******* Stockid : 0 15:16:14,861 INFO [StockTimerService] ******* Quantite : 1000 15:16:14,861 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,863 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:16:14,865 INFO [StockTimerService] ************************

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 148: Ejb Jsf Struts Flex Jasper

STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,865 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:16:14,865 INFO [StockTimerService] ******* Nom de l’article : Ballon 15:16:14,866 INFO [StockTimerService] ******* Stockid : 1 15:16:14,866 INFO [StockTimerService] ******* Quantite : 2000 15:16:14,866 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,867 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:16:14,870 INFO [STDOUT] Hibernate: select produit0_.produitid as produitid40_1_, produit0_.CATALOGUE_FK as CATALOGUE4_40_1_, produit0_.description as descript2_40_1_, produit0_.nom as nom40_1_, catalogue1_.catalogueid as catalogu1_47_0_, catalogue1_.description as descript2_47_0_, catalogue1_.nom as nom47_0_ from Produit produit0_ left outer join Catalogue catalogue1_ on produit0_.CATALOGUE_FK=catalogue1_.catalogueid where produit0_.produitid=? 15:16:14,873 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,873 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:16:14,873 INFO [StockTimerService] ******* Nom de l’article : Tablette chocolat noir 15:16:14,873 INFO [StockTimerService] ******* Stockid : 2 15:16:14,874 INFO [StockTimerService] ******* Quantite : 3000 15:16:14,874 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,876 INFO [STDOUT] Hibernate: select article0_.articleid as articleid45_, article0_.nom as nom45_, article0_.prix as prix45_, article0_.PRODUIT_FK as PRODUIT4_45_, article0_.STOCK_FK as STOCK5_45_ from Article article0_ inner join Stock stock1_ on article0_.STOCK_FK=stock1_.stockid where stock1_.stockid=? 15:16:14,878 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! *************************************** 15:16:14,878 INFO [StockTimerService] ******* Envoi d’email d’avertissement au fournisseur : 15:16:14,878 INFO [StockTimerService] ******* Nom de l’article : Tablette chocolat blanc 15:16:14,878 INFO [StockTimerService] ******* Stockid : 3 15:16:14,879 INFO [StockTimerService] ******* Quantite : 4000 15:16:14,879 INFO [StockTimerService] ************************ STOCK EPUISE OU PRESQUE ! ***************************************

Pour arrêter le timer, invoquez le client en utilisant l’URL suivante : http://localhost:8085/VenteEnLigneWeb/TestTimer?appel=stopper

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 149: Ejb Jsf Struts Flex Jasper

La consultation des logs indique que la méthode arreterTimer() a été appelée et que le timer TimerStocks n’est plus actif :

15:16:34,059 INFO [TestTimer] ** Debut TestTimer.doGet ** 15:16:34,066 INFO [StockTimerService] ------ Méthode arreterTimer() : debut ------ 15:16:34,066 INFO [StockTimerService] ------ Informations sur le timer : ------ TimerStocks 15:16:34,066 INFO [StockTimerService] ------ Prochain timeout : -- ---- Mon Nov 23 15:16:44 CET 2009 15:16:34,066 INFO [StockTimerService] ------ Temps restant : ----- - 10781 15:16:34,066 INFO [StockTimerService] ------------- Arrêt du timer TimerStocks ------------- 15:16:34,067 INFO [StockTimerService] ------ Méthode arreterTimer() : fin ------ 15:16:34,084 INFO [TestTimer] ** Fin TestTimer.doGet **

Dans JBoss, le timer est persistant : c’est­à­dire que si le timer n’a pas été manuellement arrêté et que le serveur est redémarré, le timer est toujours actif et la méthode qui gère le timeout est encore appelée. Ceci

est conforme aux spécifications des EJB 3.0 qui spécifient que les timers doivent survivre à un crash de container, un arrêt du serveur ainsi que l’activation/passivation des EJB qui leur sont associés. Les EJB Timers sont donc des objets persistants.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnTYsANlH82ICwA=-enidentnumber

Page 150: Ejb Jsf Struts Flex Jasper

Les EJB Timers de type Message Driven Bean

Dans le cas des EJB Timer de type MDB, les timers sont créés dans la méthode onMessage() qui est invoquée à l’arrivée d’un message.

Le contrôle des cartes de crédit mis en place dans le chapitre Traitement des commandes avec les Message­Driven Beans peut être repris pour illustrer le développement et l’utilisation des EJB Timers de type MDB. Mais au lieu d’utiliser le framework Quartz, vous pouvez donc utiliser le service Timer pour déclencher un timer qui exécute une tâche après expiration.

La même destination, à savoir MailConfirmationMdbTopic, peut être utilisée de nouveau. En effet, la destination est de type Topic, elle peut donc être consommée par plusieurs consommateurs.

Pour tester, il est préférable d’utiliser un timer avec un intervalle beaucoup plus court (15 secondes par exemple, au lieu de 24 heures).

package com.eni.dvtejb.metier.mdb; import java.util.Calendar; import java.util.Date; import javax.annotation.PreDestroy; import javax.annotation.Resource; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.ejb.MessageDrivenContext; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerService; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; import org.jboss.logging.Logger; @MessageDriven(mappedName = "topic/MailConfirmationMdbTopic", activationConfig = @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/MailConfirmationMdbTopic") ) public class TraitementCarteCreditMdbTimerBean implements MessageListener private static final Logger log = Logger.getLogger(TraitementCarteCreditMdbTimerBean.class); @Resource private MessageDrivenContext messageDrivenCtx; private String leMail = null; public TraitementCarteCreditMdbTimerBean() System.out.println(" --- Initialisation de l’envoi du mail depuis TraitementCarteCreditMdbTimerBean --- "); public void onMessage(Message message) TimerService timerService = messageDrivenCtx.getTimerService(); // Création d’un timer qui expire tous les jours à 4h du matin // Récupérer un calandar à la date du jour Calendar cal = Calendar.getInstance(); // Fixer l’heure à 4h du matin cal.set(Calendar.HOUR, 4);

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoQJ+Q1lH82ICwA=-enidentnumber

Page 151: Ejb Jsf Struts Flex Jasper

cal.set(Calendar.MINUTE, 00); cal.set(Calendar.SECOND, 0); // Récupérer un objet Date à partir du Calendar Date date = cal.getTime(); // Intervalle de 24 heures //long unJour = (24 * 60 * 60 * 1000); long quinzesecondes = (15 * 1000); long cinqsecondes = (5 * 1000); // Timer timer = timerService.createTimer(date, unJour, "TimerMDBCarteCredit"); // Création d’un timer de test: intervalle de 15 secondes Timer timer = timerService.createTimer(quinzesecondes, quinzesecondes, "TimerMDBCarteCredit"); if (message instanceof TextMessage) TextMessage mail = (TextMessage) message; // L’envoi d’un mail de confirmation au client est ici simulé par l’affichage d’un message au niveau des logs. try leMail = mail.getText(); catch (JMSException e) e.printStackTrace(); @Timeout public void envoyerMail(javax.ejb.Timer timer) log.info("------------ Méthode envoyerMail(javax.ejb.Timer timer) : debut --------"); String intoTimer = (String) timer.getInfo(); log.info(intoTimer); System.out.println(" TraitementCarteCreditMdbTimerBean - envoi du mail : " + leMail); System.out.println(" ------------------------- "); System.out.println(" Mail ENVOYE "); System.out.println(" ------------------------- "); @PreDestroy public void remove() System.out.println("Suppression de TraitementCarteCreditMdbTimerBean.");

En reprenant le même client producteur qu’au chapitre Traitement des commandes avec les Message­Driven Beans, à savoir la classe MailConfirmationProducteur.java, on voit après consultation des logs émis par JBoss que le timer sur l’EJB de type MDB TraitementCarteCreditMdbTimerBean est bien déclenché de manière récurrente toutes les 15 secondes, suite à l’envoi d’un message par le client.

12:35:14,602 INFO [STDOUT] Initialisation de l’envoi du mail depuis MailConfirmationMdbBean 12:35:14,602 INFO [STDOUT] ---------------- Initialisation de l’envoi du mail depuis TraitementCarteCreditMdbTimerBean ---------------- 12:35:14,602 INFO [STDOUT] MailConfirmationMdbBean - envoi du mail : Mail de confirmation pour le client. 12:35:14,602 INFO [STDOUT] ---------------------------------- 12:35:14,602 INFO [STDOUT] Mail ENVOYE 12:35:14,602 INFO [STDOUT] ---------------------------------- 12:35:29,611 INFO [TraitementCarteCreditMdbTimerBean] ------------ Méthode envoyerMail(javax.ejb.Timer timer) : debut --------

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoQJ+Q1lH82ICwA=-enidentnumber

Page 152: Ejb Jsf Struts Flex Jasper

12:35:29,611 INFO [TraitementCarteCreditMdbTimerBean] TimerMDBCarteCredit 12:35:29,611 INFO [STDOUT] TraitementCarteCreditMdbTimerBean - envoi du mail : Mail de confirmation pour le client. 12:35:29,611 INFO [STDOUT] -------------------------------------- 12:35:29,611 INFO [STDOUT] Mail ENVOYE 12:35:29,611 INFO [STDOUT] -------------------------------------- 12:35:44,620 INFO [TraitementCarteCreditMdbTimerBean] ------------ Méthode envoyerMail(javax.ejb.Timer timer) : debut -------- 12:35:44,620 INFO [TraitementCarteCreditMdbTimerBean] TimerMDBCarteCredit 12:35:44,620 INFO [STDOUT] TraitementCarteCreditMdbTimerBean - envoi du mail : Mail de confirmation pour le client. 12:35:44,620 INFO [STDOUT] --------------------------------------- 12:35:44,620 INFO [STDOUT] Mail ENVOYE 12:35:44,620 INFO [STDOUT] ---------------------------------------

Les dates proposées par le service Timer ne sont pas suffisamment flexibles pour permettre des planifications précises, par exemple tous les deuxièmes mardis du mois. Les EJB Timers pêchent donc en précision. Comme vous le verrez dans le dernier chapitre, la spécification des EJB 3.1 corrige ce manque de précision en reprenant un paramétrage similaire à celui utilisé dans le framework Quartz, c’est­à­dire des expressions de type Cron, l’utilitaire Unix.

Vous pouvez voir les services Timers qui sont actifs à partir de la console JMX et en filtrant sur l’objet jboss.ejb, dans la colonne de gauche. Puis en sélectionnant le service « persistencePolicy=database,service=EJBTimerService ».

Vous pouvez ensuite lister les timers en appelant l’opération listTimerHandles. Pour cela cliquez sur le bouton INVOKE correspondant.

Comparaison avec Quartz

Arrêt manuel des timers dans JBoss

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoQJ+Q1lH82ICwA=-enidentnumber

Page 153: Ejb Jsf Struts Flex Jasper

Pour arrêter de façon manuelle les timers, cliquez sur le bouton INVOKE au niveau de l’opération clearTimers. Cette opération efface tous les timers persistants.

Un redémarrage du serveur est ensuite nécessaire.

Il est toutefois possible dans le serveur JBoss de désactiver la persistance des timers une fois pour toute en décommentant la ligne suivante, au niveau du MBean EJBTimerService, dans le fichier <server­jboss>/server/default/deploy/ejb2­timer­service.xml :

<mbean code="org.jboss.ejb.txtimer.NoopPersistencePolicy" name="jboss.ejb:service=EJBTimerService,persistencePolicy=noop"/>

Et en changeant le type de persistance à noop au lieu de database :

<!-- An EJB Timer Service that is Tx aware --> <mbean code="org.jboss.ejb.txtimer.EJBTimerServiceImpl" name="jboss.ejb:service=EJBTimerService"> <attribute name="TimerIdGeneratorClassName">org.jboss.ejb.txtimer.BigIntegerTimerI

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoQJ+Q1lH82ICwA=-enidentnumber

Page 154: Ejb Jsf Struts Flex Jasper

dGenerator</attribute> <attribute name="TimedObjectInvokerClassName">org.jboss.ejb.txtimer.TimedObjectInv okerImpl</attribute> <depends optional-attribute- name="RetryPolicy">jboss.ejb:service=EJBTimerService,retryPolicy=fixedD elay</depends> <depends optional-attribute-

name="PersistencePolicy">jboss.ejb:service=EJBTimerService,persistenceP

olicy=noop</depends>

<depends optional-attribute-name="TransactionManagerFactory" proxy- type="org.jboss.tm.TransactionManagerFactory"> jboss:service=TransactionManager </depends> </mbean>

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoQJ+Q1lH82ICwA=-enidentnumber

Page 155: Ejb Jsf Struts Flex Jasper

LES EJB Timers et les transactions

Quand un timer effectue un traitement en base, il est plus que souhaitable de permettre à l’application de pouvoir contrôler ce traitement au sein d’une transaction. Cela est évidemment possible avec les EJB Timers.

Si la création d’un timer est faite dans une transaction, le timer n’est considéré créé qu’au moment où la transaction se termine. Ce qui signifie que la méthode timeout de ce timer ne peut être déclenchée avant que la transaction ne se termine.

Lors du rollback d’une transaction, tout timer créé dans la portée de cette transaction est annulé. Il en est de même de l’annulation d’un timer : l’action d’annulation d’un timer (avec la méthode cancel()) est annulée si elle est effectuée dans la portée d’une transaction qui est rollbackée.

La méthode de timeout peut être exécutée dans une transaction. Les attributs transactionnels possibles sur la méthode sont : REQUIRES et REQUIRED_NEW.

L’EJB Timer StockTimerService peut ainsi être modifié en ajoutant des attributs transactionnels de la manière suivante :

package com.eni.dvtejb.metier.sessions; import java.util.Iterator; import java.util.List; import javax.annotation.Resource; import javax.ejb.Stateless; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.ejb.TransactionManagement; import javax.ejb.TransactionManagementType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Stock; @Stateless

@TransactionManagement(value=TransactionManagementType.CONTAINER)

@TransactionAttribute(value=TransactionAttributeType.REQUIRED)

public class StockTimerService implements StockTimerServiceRemote ...

@TransactionAttribute(value=TransactionAttributeType.REQUIRES_NEW)

public void verifierStocks() ... @Timeout

@TransactionAttribute(value=TransactionAttributeType.REQUIRES_NEW)

public void maintenance(javax.ejb.Timer timer) ...

Dans l’exemple, la transaction de la méthode qui gère le timeout est de type REQUIRES_NEW, ce qui veut dire que le container créera une transaction et suspendra la transaction courante, s’il y en a une.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mq953xdlH82ICwA=-enidentnumber

Page 156: Ejb Jsf Struts Flex Jasper

Définition

Un interceptor est une méthode qui intercepte l’appel d’une méthode métier.

Les interceptors fonctionnent à la manière des filtres avec les servlets.

Ils sont automatiquement appelés, juste avant que les méthodes métiers du bean soient appelées.

Ils sont associés à la programmation orientée aspect (AOP) puisque les interceptors ajoutent des comportements à la volée sur des appels de méthodes.

Ils peuvent être appliqués à des méthodes métiers dans des session beans (stateful et stateless) et dans des message­driven beans.

On peut définir une méthode interceptor dans une classe bean ou dans une classe dédiée.

La signature d’une méthode interceptor est la suivante :

@javax.ejb.AroundInvoke public Object [nomMethode](javax.ejb.InvocationContext ctx) throws java.lang.Exception

Une classe interceptor ou une classe bean ne peut avoir qu’une seule méthode annotée avec l’annotation @AroundInvoke.

On distingue 3 types d’interceptors :

Les interceptors au niveau de la classe.

Les interceptors au niveau de la méthode.

Les interceptors par défaut qui sont définis via XML uniquement. Ils s’appliquent à tous les EJB de type session bean et MDB qui sont présents dans le fichier ejb­jar.xml. L’annotation @ExcludeDefaultInterceptors permet de désactiver ces interceptors par défaut sur une méthode ou sur une classe.

Lorsque plusieurs interceptors sont présents dans une application, leur ordre d’invocation est le suivant :

Comme indiqué dans le schéma, les interceptors définis à l’intérieur du bean sont les derniers à être appelés.

Les classes interceptors ont le même cycle de vie que les EJB qu’elles interceptent.

Les avantages des interceptors sont multiples :

Un contrôle plus fin dans le déroulement d’une méthode.

La possibilité d’ajouter des fonctionnalités à une méthode sans en modifier le code.

On peut de façon élégante analyser et manipuler des paramètres et autoriser ou interdire l’exécution d’une méthode métier.

On arrive ainsi à gérer des fonctionnalités techniques (logging, traçage, sécurité) de façon transparente dans les méthodes métiers.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MgI3DyBlH82ICwA=-enidentnumber

Page 157: Ejb Jsf Struts Flex Jasper

L’interface InvocationContext

L’interface javax.interceptor.InvocationContext permet de propager un état à travers une chaîne d’interceptors. Cette interface fournit des méthodes qui permettent d’identifier le bean concerné ainsi que la méthode interceptée.

package javax.interceptor; public interface InvocationContext public Object getBean(); public java.lang.reflect.Method getMethod(); public Object[] getParameters(); public void setParameters(Object[] params); public java.util.Map getContextData(); public Object proceed() throws Exception;

Pour revenir à la méthode interceptée, il faut toujours appeler la méthode proceed() dans l’appel de l’objet invocationContext. Sinon,il n’y a pas de retour vers la méthode interceptée du bean, pas d’appels d’autres méthodes interceptor et pas d’appels de méthodes du cycle de vie. La méthode getContextData() permet de faire communiquer des interceptors entre eux au travers d’une Map (un objet qui associe des clés à des valeurs) pour des échanges de données.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrhXBiplH82ICwA=-enidentnumber

Page 158: Ejb Jsf Struts Flex Jasper

Le design pattern Chain of Responsibility (CoR)

Les interceptors sont une forme du design pattern Chain of Responsibility.Ce design pattern concerne une chaîne d’objets qui sont liés à travers des références indirectes pour permettre à un objet de traiter une requête ou la passer à un autre objet dans la chaîne. C’est ce qui se passe avec les interceptors dans les EJB : la méthode proceed() provoque l’appel de la prochaine méthode interceptor dans la chaîne. Quand la dernière méthode interceptor AroundInvoke est appelée, la méthode proceed() appelle la méthode métier du bean. La flèche ci­dessous illustre les enchaînements dans le cas de plusieurs intercepteurs appliqués à un bean :

@Interceptors(Interceptor1.class, Interceptor2.class, Interceptor3.class) public class MonBean ... public void methode() ...

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mh1ewDJlH82ICwA=-enidentnumber

Page 159: Ejb Jsf Struts Flex Jasper

Intercepter la création d’un nouveau client

Un cas d’utilisation concret des interceptors dans l’application peut être par exemple la vérification en base d’une adresse email lorsqu’un client s’inscrit. Si le client rentre une adresse email qui existe déjà en base, cela signifie qu’un autre client possède déjà cette adresse email. Dans ce cas il est impératif d’annuler l’inscription et de notifier le client que cette adresse email est déjà utilisée et qu’il doit en entrer une autre.

Le schéma suivant illustre l’interception de la création d’un nouvel utilisateur (de type client) :

Dans le projet VenteEnLigneEJB, créez le package com.eni.dvtjeb.metier.interceptors et ajoutez­y la classe interceptor NouvelUtilisateurInterceptor.java :

package com.eni.dvtjeb.metier.interceptors; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.PostActivate; import javax.ejb.PrePassivate; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; import javax.persistence.EntityManager;

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFkXj1lH82ICwA=-enidentnumber

Page 160: Ejb Jsf Struts Flex Jasper

import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Client; public class NouvelUtilisateurInterceptor private static final Logger log = Logger.getLogger(NouvelUtilisateurInterceptor.class); @PersistenceContext private EntityManager em; @AroundInvoke public Object controleEmail(InvocationContext ctx) throws Exception log.info(" ------------ Debut d’interception ------------ "); try /* * 1. Récuperer le client en cours de création : c’est le paramètre de la méthode. * 2. Vérifier que son email n’existe pas déjà en base * 3. Annuler l’inscription si l’email existe déjà, sinon poursuivre l’insertion */ Client client = (Client)ctx.getParameters()[0]; String email = client.getEmail(); log.info("email vaut : " + email); Client doublon = (Client)em.createQuery("select c from Client c where c.email = :email").setParameter("email", email).getSingleResult(); if (null != doublon) log.info(" ----------- Cette adresse email existe déjà !"); throw new AdresseEmailException("Cette adresse email existe déjà !"); catch (NoResultException nre) // La méthode getSingleResult() lance une exception de type NoResultException si la requête ne retourne aucun résultat log.info(" ----------- Exception NoResultException: cet email n’existe pas encore en base ------------- "); catch(Exception e) throw e; return ctx.proceed(); @PostActivate public void postActivate(InvocationContext ic) log.info("Méthode appelée : " + ic.getMethod()); @PrePassivate public void prePassivate(InvocationContext ic) log.info("Méthode appelée : " + ic.getMethod()); @PreDestroy public void preDestroy(InvocationContext ic) log.info("Le bean intercepté : " + ic.getTarget() + "

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFkXj1lH82ICwA=-enidentnumber

Page 161: Ejb Jsf Struts Flex Jasper

va être supprimé."); @PostConstruct public void postConstruct(InvocationContext ic) log.info("Le bean intercepté : " + ic.getTarget() + " a été crée.");

Cette classe contient une méthode controleEmail(InvocationContext ctx) avec l’annotation @javax.interceptor.AroundInvoke qui ne prend aucun argument. Cette méthode est invoquée par le container et doit pouvoir gérer les exceptions en lançant une exception, la clause « throws Exception » est donc obligatoire. Elle doit retourner Object. Dans un premier temps elle récupère le premier paramètre de la méthode interceptée grâce à la méthode getParameters() de l’interface InvocationContext. Ce paramètre est de type Client. Dans un deuxième temps elle vérifie si l’email de ce client est déjà présent en base. Si c’est le cas, elle jette une exception de type AdresseEmailException. Si ce n’est pas le cas elle retourne la méthode javax.Interceptor.InvocationContext.proceed(), qui repasse le contrôle à la méthode du bean intercepté. Sauf si d’autres interceptors sont mis en place, auquel cas elle passe le contrôle au prochain interceptor dans la chaîne.

Les méthodes du cycle de vie annotées avec @PostActivate et @PrePassivate ne s’appliquent pas dans cet exemple dans la mesure où le bean intercepté est un session bean de type stateless.

Le bean intercepté et donc à modifier est le stateless session bean ClientDAOImpl.java :

package com.eni.dvtejb.metier.sessions; import java.util.List; import javax.ejb.Stateless; import javax.interceptor.ExcludeClassInterceptors; import javax.interceptor.Interceptors; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtjeb.metier.interceptors.NouvelUtilisateurInterceptor; @Interceptors(NouvelUtilisateurInterceptor.class) @Stateless public class ClientDAOImpl implements ClientDAO @PersistenceContext private EntityManager em; private Logger log = Logger.getLogger(this.getClass()); public void save(Client client) log.debug("Enregistrement du client :" + client); em.persist(client); @ExcludeClassInterceptors public void merge(Client client) em.merge(client); @ExcludeClassInterceptors public List findAll() log.debug("Recherche de tous les clients"); return em.createQuery("from Client").getResultList(); @ExcludeClassInterceptors public Client findById(Long id) return em.find(Client.class, id); @ExcludeClassInterceptors public List findByNom(String nom)

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFkXj1lH82ICwA=-enidentnumber

Page 162: Ejb Jsf Struts Flex Jasper

log.debug("Recherche de clients par nom"); return em.createQuery("from Client c where c.nom = :nom").setParameter("nom", nom).getResultList(); @ExcludeClassInterceptors public List findByPrenom(String prenom) log.debug("recherche par prenom"); Query q = em.createNamedQuery("Utilisateur.findByPrenom"); q.setParameter("lePrenom", prenom); List<Client> resultatsNamedQuery = q.getResultList(); return resultatsNamedQuery;

Les modifications effectuées sur ce bean sont les suivantes :

Ajout de l’annotation @Interceptors(NouvelUtilisateurInterceptor.class) au niveau de la classe. Cette annotation spécifie un interceptor qui est définit dans une autre classe. Elle peut prendre un ou plusieurs interceptors en paramètres. Ces interceptors sont appelés dans le même ordre qu’ils sont définis.

Ajout de l’annotation @ExcludeClassInterceptors sur toutes les méthodes du bean sauf la méthode void save(Client client). Elle permet de désactiver les Interceptors de niveau classe sur les méthodes annotées.

Une exception de type AdresseEmailException est levée si l’adresse email existe déjà en base :

package com.eni.dvtjeb.metier.interceptors; public class AdresseEmailException extends Exception AdresseEmailException(String message) super ( message );

Il ne reste plus qu’à gérer cette exception au niveau de la méthode creerNouvelUtilisateur() de la classe Action UtilisateurAction dans le projet VenteEnLigneWebStruts2 mais l’essentiel du code métier a été développé dans une classe Interceptor.

Extrait de la classe UtilisateurAction.java :

public String creerNouvelUtilisateur() throws Exception ... try clientDAO.save(client); catch (Exception e ) addFieldError("client.email", "Cette addresse email est déjà utilisée. Veuillez rentrez une adresse email différente."); return INPUT; return SUCCESS;

Un message d’erreur est affiché si l’utilisateur entre une adresse email qui existe déjà en base. Et l’inscription est donc annulée.

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFkXj1lH82ICwA=-enidentnumber

Page 163: Ejb Jsf Struts Flex Jasper

Une alternative à l’annotation de la classe toute entière consiste à annoter uniquement la méthode à intercepter, de la manière suivante :

@Stateless public class ClientDAOImpl implements ClientDAO ... @Interceptors(NouvelUtilisateurInterceptor.class) public void save(Client client) log.debug("Enregistrement du client :" + client); em.persist(client); ...

Le tableau suivant récapitule les cibles possibles des annotations des interceptors :

Annotations / Cibles Méthode Classe

javax.interceptor.AroundInvoke x

javax.interceptor.Interceptors x x

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFkXj1lH82ICwA=-enidentnumber

Page 164: Ejb Jsf Struts Flex Jasper

Cette technique d’interception se rapproche de la programmation par aspect (AOP) : le code métier est séparé du code d’interception. Dans la terminologie AOP, un join point est un point dans l’exécution du programme. Un pointcut est une expression qui permet de sélectionner un ou plusieurs join points. Un Aspect est un module qui encapsule des pointcuts et des advices. Un Advice est du code qui est exécuté dans un join point sélectionné par un pointcut.

Par analogie, une méthode interceptor joue le rôle d’un advice et la classe EJB qui contient cet interceptor, joue le rôle d’un aspect.

javax.interceptor.ExcludeClassInterceptors x

javax.interceptor.ExcludeDefaultInterceptors x x

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFkXj1lH82ICwA=-enidentnumber

Page 165: Ejb Jsf Struts Flex Jasper

Contrôle des stocks des articles

Les articles disponibles dans le magasin disposent d’un stock qui doit évoluer en fonction des commandes. Après validation du paiement la commande est effective et peut être insérée en base. Une méthode interceptor peut être utile pour diminuer le nombre d’articles dès qu’une commande est effectuée. Cette conception permet donc de déléguer le contrôle des stocks à une classe interceptor et de diminuer les impacts sur le code du bean si la gestion des stocks doit évoluer (par exemple décider de doubler la quantité d’articles à soustraire dans le cas d’afflux de commandes afin de prévenir une pénurie de stocks et de remonter ainsi des alertes plus rapidement).

L’entity manager est injecté dans l’interceptor CommanderInterceptor. Dans la méthode diminuerQuantiteStock(InvocationContext ctx), la liste des articles du panier est récupérée et parcourue. Pour chaque article dans le panier, la quantité demandée est récupérée afin de calculer la nouvelle quantité d’articles. L’entity bean Stock est ensuite persisté en base.

package com.eni.dvtjeb.metier.interceptors; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Iterator; import javax.ejb.PostActivate; import javax.ejb.PrePassivate; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Stock; import com.eni.dvtejb.metier.services.ArticlePanier; public class CommanderInterceptor private static final Logger log = Logger.getLogger(CommanderInterceptor.class); @PersistenceContext private EntityManager em; @AroundInvoke public Object diminuerQuantiteStock(InvocationContext ctx) throws Exception log.info(" ------------ Debut d’interception ------------ "); try ArrayList<ArticlePanier> articlesPanier = (ArrayList<ArticlePanier>)ctx.getParameters()[1]; ArticlePanier artPanier = new ArticlePanier(); for (Iterator it = articlesPanier.iterator (); it.hasNext (); ) artPanier = (ArticlePanier)it.next (); Long stockId = artPanier.getArticle().getStockFK().getStockid(); Stock stock = em.find(Stock.class, stockId); BigDecimal quantiteActuelle = stock.getQuantite(); BigDecimal quantiteCommandee = artPanier.getQuantite(); BigDecimal nouvelleQuantite = quantiteActuelle.subtract(quantiteCommandee); stock.setQuantite(nouvelleQuantite); em.persist(stock); log.info(" ------------ Fin d’interception ------------ "); return ctx.proceed();

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoESHkVlH82ICwA=-enidentnumber

Page 166: Ejb Jsf Struts Flex Jasper

catch(Exception e) throw e; @PostActivate public void postActivate(InvocationContext ic) log.info("Méthode appelée : " + ic.getMethod()); @PrePassivate public void prePassivate(InvocationContext ic) log.info("Méthode appelée : " + ic.getMethod()); @PreDestroy public void preDestroy(InvocationContext ic) log.info("Le bean intercepté : " + ic.getTarget() + " va être supprimé."); @PostConstruct public void postConstruct(InvocationContext ic) log.info("Le bean intercepté : " + ic.getTarget() + " a été crée.");

Côté bean, l’annotation @Interceptors est cette fois­ci utilisée au niveau de la méthode uniquement. Ceci évite d’avoir à annoter les autres méthodes du bean PanierBean avec l’annotation @ExcludeClassInterceptors pour les inhiber.

@Stateful @Remote (PanierBeanRemote.class) public class PanierBean implements PanierBeanRemote, Serializable ... @Interceptors(CommanderInterceptor.class) public void commander(Client client, ArrayList<ArticlePanier> articlesPanier, String numCC, String typeCC, java.sql.Date expirationDate) log.info("Debut methode commander"); ... log.info("Fin methode commander");

La consultation des logs montre bien que la méthode du bean est interceptée et surtout que la méthode interceptor est dans la même transaction, ce qui a pour effet de ne mettre à jour la colonne QUANTITE dans la table STOCK qu’après insertion réussie de la commande. C’est un comportement plus que souhaitable dans la mesure où les quantités en stock seraient faussées si elles étaient mises à jour alors que la commande a échouée.

La méthode interceptor (annotée avec l’annotation @Aroundinvoke) s’exécute en effet dans le même contexte transactionnel que la méthode du bean pour laquelle elle a été définie.

14:58:28,796 INFO [CommanderInterceptor] ------------ Debut d’interception ------------ 14:58:28,797 INFO [STDOUT] Hibernate: select stock0_.stockid as stockid46_0_, stock0_.quantite as quantite46_0_ from Stock stock0_ where stock0_.stockid=? 14:58:28,800 INFO [STDOUT] Hibernate: select stock0_.stockid as stockid46_0_, stock0_.quantite as quantite46_0_ from Stock stock0_ where stock0_.stockid=? 14:58:28,802 INFO [CommanderInterceptor] ------------ Fin d’interception ------------ 14:58:28,802 INFO [PanierBean] Debut methode commander 14:58:28,802 INFO [STDOUT] Hibernate: select client0_.utilisateurid as utilisat2_41_1_, client0_.ADRESSE_FK as ADRESSE11_41_1_, client0_.email as email41_1_, client0_.fax as fax41_1_,

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoESHkVlH82ICwA=-enidentnumber

Page 167: Ejb Jsf Struts Flex Jasper

client0_.login as login41_1_, client0_.nom as nom41_1_, client0_.password as password41_1_, client0_.prenom as prenom41_1_, client0_.telephone as telephone41_1_, client0_.titre as titre41_1_, adresse1_.adresseid as adresseid43_0_, adresse1_.codepostal as codepostal43_0_, adresse1_.departement as departem3_43_0_, adresse1_.numero as numero43_0_, adresse1_.pays as pays43_0_, adresse1_.rue as rue43_0_, adresse1_.ville as ville43_0_ from HR.UTILISATEUR client0_ left outer join Adresse adresse1_ on client0_.ADRESSE_FK=adresse1_.adresseid where client0_.utilisateurid=? and client0_.TYPE_UTIL=’C’ 14:58:28,806 INFO [STDOUT] Hibernate: select adresse0_.adresseid as adresseid43_0_, adresse0_.codepostal as codepostal43_0_, adresse0_.departement as departem3_43_0_, adresse0_.numero as numero43_0_, adresse0_.pays as pays43_0_, adresse0_.rue as rue43_0_, adresse0_.ville as ville43_0_ from Adresse adresse0_ where adresse0_.adresseid=? 14:58:28,809 INFO [PanierBean] Traitement des articles du panier 14:58:28,812 INFO [STDOUT] Hibernate: select lignecommande_seq.nextval from dual 14:58:28,817 INFO [STDOUT] Hibernate: select article_.articleid, article_.image as image45_, article_.nom as nom45_, article_.prix as prix45_, article_.PRODUIT_FK as PRODUIT5_45_, article_.STOCK_FK as STOCK6_45_ from Article article_ where article_.articleid=? 14:58:28,850 INFO [STDOUT] Hibernate: select article_.articleid, article_.image as image45_, article_.nom as nom45_, article_.prix as prix45_, article_.PRODUIT_FK as PRODUIT5_45_, article_.STOCK_FK as STOCK6_45_ from Article article_ where article_.articleid=? 14:58:28,854 INFO [PanierBean] Insertion de la commande 14:58:28,854 INFO [STDOUT] Hibernate: select commande_seq.nextval from dual 14:58:28,856 INFO [PanierBean] Fin methode commander 14:58:28,877 INFO [STDOUT] Hibernate: insert into Lignecommande (ARTICLE_FK, quantite, lignecommandeid) values (?, ?, ?) 14:58:28,880 INFO [STDOUT] Hibernate: insert into Lignecommande (ARTICLE_FK, quantite, lignecommandeid) values (?, ?, ?) 14:58:28,885 INFO [STDOUT] Hibernate: insert into Commande (ADRESSE_FK, DATE_EXPIRATION_CARTECREDIT, datecommande, NUMERO_CARTECREDIT, TYPE_CARTECREDIT, UTILISATEUR_FK, commandeid) values (?, ?, ?, ?, ?, ?, ?) 14:58:28,891 INFO [STDOUT] Hibernate: update Stock set quantite=? where stockid=? 14:58:28,893 INFO [STDOUT] Hibernate: update Stock set quantite=? where stockid=? 14:58:28,896 INFO [STDOUT] Hibernate: insert into LIGNECOMMANDE_TJ (COMMANDE_FK, LIGNECOMMANDE_FK) values (?, ?) 14:58:28,896 INFO [STDOUT] Hibernate: insert into LIGNECOMMANDE_TJ (COMMANDE_FK, LIGNECOMMANDE_FK) values (?, ?)

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoESHkVlH82ICwA=-enidentnumber

Page 168: Ejb Jsf Struts Flex Jasper

Interface d’administration

Une application Web est souvent constituée d’une partie publique qui ne requiert pas d’authentification et d’une partie privée qui requiert une authentification. Elle est également complétée par une interface d’administration pour faciliter sa maintenance au quotidien. L’accès à l’interface d’administration requiert l’authentification d’un utilisateur autorisé par un couple login/mot de passe. Le rôle de cet utilisateur peut être Administrateur ou Gestionnaire.

Les besoins sont les suivants :

L’administration permet de gérer les articles et les utilisateurs.

Les administrateurs ont le droit de tout faire.

Il existe 3 groupes (rôles) d’utilisateurs : Administrateur, Gestionnaire et Client. Un utilisateur possède un de ces 3 rôles.

Les gestionnaires peuvent ajouter des articles, modifier les prix et les quantités des articles. Ils peuvent aussi faire des recherches de clients mais ne peuvent pas les modifier, supprimer ou ajouter.

Chaque utilisateur ne peut appartenir qu’à un seul groupe.

Les clients n’ont évidemment pas accès à l’interface d’administration.

L’authentification est mise en place dans les chapitres Struts 2 (version Struts 2) et Développement d’un client avec JSF 2 (version JSF 2). Une fois que l’utilisateur est authentifié, il possède un rôle qui lui attribue des permissions sur des ressources. Ces permissions sont de type CRUD (Create, Read, Update et Delete). Le tableau suivant récapitule les actions permises suivant les utilisateurs en fonction de leurs rôles :

La page d’accueil de l’interface d’administration développée avec Struts 2 se présente de la façon suivante :

Modifier, ajouter et supprimer un article

Ajouter et supprimer un client

Rechercher un client

Administrateur Oui Oui Oui

Gestionnaire Oui Non Oui

Client Non Non Non

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkFMcVBlH82ICwA=-enidentnumber

Page 169: Ejb Jsf Struts Flex Jasper

Authentication, autorisation et rôles avec JAAS et les EJB 3

L’API JAAS (Java Authentication and Authorization Service) est une API standard pour la gestion de l’authentification et des autorisations et est supportée par les serveurs d’application certifiés Java EE.

Au niveau du conteneur, avec les EJB 3, les phases d’authentication et d’autorisation sont simplifiées avec des annotations qui permettent de mettre en place des restrictions d’accès sur les classes des beans et leurs méthodes suivant les rôles.

Il existe plusieurs mécanismes de gestion des rôles au cours de la phase d’autorisation, en utilisant l’API JAAS. Les 3 mécanismes décrits dans ce chapitre sont les suivants :

autorisation selon des rôles définis dans des fichiers properties ;

autorisation selon des rôles définis en base ;

autorisation selon des rôles définis dans un annuaire LDAP.

Les rôles peuvent être définis en base. La table UTILISATEUR contient une colonne TYPE_UTIL dont les valeurs possibles correspondent aux trois rôles possibles de l’application : C (Client), A (Administrateur) et G (Gestionnaire). Il faut récupérer les rôles définis en base pour les associer aux utilisateurs lorsqu’ils sont connectés.

Une alternative au stockage des rôles en base est l’utilisation des fichiers users properties et roles.properties. Enfin, les utilisateurs peuvent être stockés dans un annuaire LDAP. Le serveur d’application JBoss supporte ces différentes méthodes en fournissant plusieurs implémentations du module de login JAAS pour stocker et accéder aux informations des utilisateurs et des rôles. Ces modules sont implémentés dans JBoss SX, le framework de sécurité de JBoss.

1. Gestion des rôles avec des fichiers de propriétés

Le serveur JBoss implémente donc JAAS et fournit des services d’authentication et d’autorisation sous la forme de plusieurs modules dont le module UsersRolesLoginModule.

Le schéma suivant illustre le cas où les rôles et les logins sont définis dans des fichiers de properties et les contrôles de permissions qui sont fait au niveau des méthodes des EJB, à l’intérieur d’un domaine de sécurité :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 170: Ejb Jsf Struts Flex Jasper

Dans le cas de la création d’un stateless session bean dédié à la gestion des utilisateurs (recherche, ajout, suppression, modification) à partir d’une interface d’administration, il est souhaitable de restreindre l’accès aux méthodes de ce bean aux utilisateurs dont les rôles sont A (Administrateur) ou G (Gestionnaire). Les étapes suivantes décrivent la mise en place d’une authentication et d’une authentification reposant sur JAAS, de la création du stateful session bean au test JUnit, en passant par la configuration des fichiers de properties.

Créez le stateful session bean UtilisateurDAOBean.java à partir de l’interface UtilisateurDAOBeanRemote.java, dans le projet VenteEnLigneEJB.

L’interface UtilisateurDAOBeanRemote.java :

package com.eni.dvtejb.metier.sessions; import java.util.List; import javax.ejb.Remote; import com.eni.dvtejb.metier.entities.Utilisateur; @Remote public interface UtilisateurDAOBeanRemote public List<Utilisateur> rechercherTous(); public void sauver(Utilisateur person); public void supprimer(long id); public Utilisateur rechercher(long id);

Les restrictions sur les méthodes du stateless session bean UtilisateurDAOBean sont conformes aux besoins : les gestionnaires (G) ne peuvent faire que des recherches d’utilisateurs alors que les administrateurs (A) peuvent tout faire.

Les stateful session bean UtilisateurDAOBean.java :

package com.eni.dvtejb.metier.sessions; import java.util.List; import javax.annotation.security.RolesAllowed; import javax.ejb.Stateful;

Étape 1

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 171: Ejb Jsf Struts Flex Jasper

import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.log4j.Logger; import org.jboss.ejb3.annotation.SecurityDomain; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Utilisateur; @SecurityDomain("venteEnLigne_domaine") @RolesAllowed("A", "G") @Stateful public class UtilisateurDAOBean implements UtilisateurDAOBeanRemote private static final Logger log = Logger.getLogger(UtilisateurDAOBean.class); @PersistenceContext private EntityManager em; @RolesAllowed("A", "G") // Administrateurs et gestionnaires sont habilités à lister les utilisateurs public List<Utilisateur> rechercherTous() log.info("Debut methode rechercherTous()" ); Query query = em.createQuery("select u from Utilisateur u where type_util = ’C’"); return query.getResultList(); @RolesAllowed("A") // Seuls les administrateurs sont habilités à modifier des utilisateurs public void sauver(Utilisateur utilisateur) log.info("Debut methode sauver()" ); Long UtilisateurId = utilisateur.getUtilisateurid(); if (UtilisateurId.toString() == null) // nouveau em.persist(utilisateur); else // modification em.merge(utilisateur); @RolesAllowed("A") // Seuls les administrateurs sont habilités à supprimer des utilisateurs public void supprimer(long utilisateurid) log.info("Debut methode supprimer()" ); log.info("utilisateurid vaut : " + utilisateurid ); Utilisateur utilisateur = rechercher(utilisateurid); if (utilisateur != null) log.info("Utilisateur trouve !" ); em.remove(utilisateur); @RolesAllowed("A", "G") // Administrateurs et gestionnaires sont habilités à lister les utilisateurs public Utilisateur rechercher(long utilisateurid) log.info("Debut methode rechercher()" ); Utilisateur ut = null; ut = em.find(Utilisateur.class, utilisateurid); return ut;

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 172: Ejb Jsf Struts Flex Jasper

Conformément aux spécifications, ces annotations ne peuvent s’appliquer que sur la classe ou les méthodes du bean. Elles ne peuvent pas s’appliquer dans l’interface du bean.

L’annotation @org.jboss.ejb3.annotation.SecurityDomain est une extension JBoss qui permet de spécifier le domaine de sécurité utilisé par JBoss pour l’authentication et l’autorisation. Celui­ci est définit dans le fichier login­config.xml, à l’intérieur de la balise <application­policy> qui définit la politique de sécurité.

Les permissions sur les méthodes peuvent se faire au niveau du descripteur de déploiement ejb­jar.xml. On parle alors d’autorisation déclarative. Cependant, cette autorisation déclarative est simplifiée avec le package javax.annotation.security. Il introduit plusieurs annotations EJB 3 de sécurité, qui peuvent être appliquées au niveau d’une classe ou/et au niveau d’une méthode. Le tableau suivant montre le niveau d’application de ces annotations :

Description des annotations :

L’annotation @javax.annotation.security.DeclareRoles déclare les rôles définis par l’application. Elle peut avoir pour paramètre une liste de rôles. Elle est utilisée par la méthode EJBContext.isCallerInRole(String nomRole) décrite à la section Gestion des rôles à partir d’un annuaire LDAP de ce chapitre.

L’équivalent XML est :

<ejb-jar version="3.0"> ... <security-role> <role-name> Gestionnaire </role-name> </security-role> ... </ejb-jar>

L’annotation @javax.annotation.security.RunAs permet de mapper le rôle de l’application pendant son exécution à un autre rôle existant.

L’équivalent XML, dans le cas d’une servlet :

<web-app> ... servlet> <servlet-name>nomServlet</servlet-name> <run-as> Gestionnaire </run-as> </servlet> ... </web-app>

L’équivalent XML, dans le cas d’un session bean :

<ejb-jar version="3.0"> ... <session> <ejb-name> UtilisateurDAOBeanRemote</ ejb-name > ... <security-identity> <run-as> <role-name> Gestionnaire</role-name> </run-as> </security-identity>

Annotation Applicable au niveau de la classe Applicable au niveau de la méthode

@DeclareRoles Oui Non

@DenyAll Oui Oui

@PermitAll Oui Oui

@RolesAllowed Oui Oui

@RunAs Oui Non

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 173: Ejb Jsf Struts Flex Jasper

... </session> ... </ejb-jar>

L’annotation @javax.annotation.security.PermitAll permet d’autoriser :

Tous les rôles à invoquer une méthode si elle est appliquée au niveau de la méthode.

Tous les rôles à invoquer toutes les méthodes d’une classe si elle est appliquée au niveau de la classe.

L’équivalent XML oblige à lister tous les rôles :

<ejb-jar version="3.0"> ... <method-permission> <role-name>Administrateur</role-name> <method> <ejb-name> UtilisateurDAOBeanRemote </ejb-name> <method-name>sauver</method-name> </method> </method-permission> <method-permission> <role-name>Gestionnaire</role-name> <method> <ejb-name> UtilisateurDAOBeanRemote </ejb-name> <method-name>sauver</method-name> </method> </method-permission> <method-permission> <role-name>Client</role-name> <method> <ejb-name> UtilisateurDAOBeanRemote </ejb-name> <method-name>sauver</method-name> </method> </method-permission> ... </ejb-jar>

L’annotation @javax.annotation.security.DenyAll permet d’interdire

Tous les rôles d’invoquer une méthode si elle est appliquée au niveau de la méthode.

Tous les rôles d’invoquer toutes les méthodes d’une classe si elle est appliquée au niveau de la classe.

L’annotation @javax.annotation.security.RolesAllowed permet de spécifier les rôles qui peuvent

Accéder une méthode si elle est appliquée au niveau de la méthode.

Accéder toutes les méthodes si elle est appliquée au niveau de la classe.

Paramétrez le fichier login­config.xml.

Ce fichier de configuration JBoss se trouve dans server\default\conf\login­config.xml.

Dans ce fichier doit être défini une configuration d’authentification qui comprend :

un domaine de sécurité (venteEnLigne_domaine) ;

deux fichiers de properties pour stocker les mots de passe et les rôles ;

le module d’authentification qui sera utilisé.

Étape 2

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 174: Ejb Jsf Struts Flex Jasper

<?xml version=’1.0’?> <!-- The XML based JAAS login configuration read by the org.jboss.security.auth.login.XMLLoginConfig mbean. Add an application-policy element for each security domain. --> <policy> ... <application-policy name="venteEnLigne_domaine">

<authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required"> <module-option name="usersProperties">props/venteEnLigne-users.properties</module-option>

<module-option name="rolesProperties">props/venteEnLigne-roles.properties</module-option>

<module-option name="securityDomain">java:/jaas/venteEnLigne_domaine</module-option>

</login-module> </authentication> </application-policy> </policy>

Créez et ajoutez les fichiers de propriétés suivant dans le répertoire server\default\conf\props\.

Fichier venteEnLigne­users.properties :

Jacques=JacquesMDP Irene=IreneMDP Robert=RobertMDP

Fichier venteEnLigne­roles.properties :

Jacques=C Irene=A Robert=G

Ces fichiers ont été déclarés dans le fichier server\default\conf\login­config.xml à l’étape précédente.

Le fichier VenteEnLigne­roles.properties contient une ligne par utilisateur, selon le format suivant : loginUtilisateur : nomRole.

Le fichier VenteEnLigne­users.properties contient une ligne par utilisateur, selon le format suivant : loginUtilisateur : passwordUtilisateur.

Créez le fichier META_INF/jboss.xml.

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_5_0.dtd"> <jboss> <security-domain>java:/jaas/venteEnLigne_domaine</security-domain> </jboss>

Dans le cas du développement d’une application Web dans JBoss (comme c’est le cas des clients Struts 2 et JSF 2 dans les chapitres 10 et 11), il faut ajouter un fichier jboss­web.xml dans le répertoire WEB­INF. Ce

fichier permet de faire le lien entre l’application Web et le serveur JBoss. Puis, à l’intérieur des classes, l’utilisation de la classe org.jboss.web.tomcat.security.login.WebAuthentication est nécessaire pour s’authentifier via le container web.

Voici un extrait de la méthode verifierLogin() du managed bean LoginBean du projet VenteEnLigneWebJSF2 :

Étape 3

Étape 4

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 175: Ejb Jsf Struts Flex Jasper

WebAuthentication pwl = new WebAuthentication(); pwl.login(login, password);

Le framework de tests JUnit convient parfaitement pour la mise en place de tests d’authentication. Dans le projet VenteEnLigneClient, créez la classe de test JUnit qui va permettre de tester l’authentication et l’autorisation :

Le code de la classe JUnit est le suivant :

package com.eni.dvtejb.tests; import java.io.FileInputStream; import java.math.BigDecimal; import java.util.Properties; import javax.ejb.EJBAccessException; import javax.naming.InitialContext; import junit.framework.TestCase; import org.apache.log4j.Logger; import org.jboss.security.client.SecurityClient; import org.jboss.security.client.SecurityClientFactory; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test;

Étape 5

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 176: Ejb Jsf Struts Flex Jasper

import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.sessions.UtilisateurDAOBeanRemote; public class UtilisateurDAOBeanTest extends TestCase SecurityClient securityClient; UtilisateurDAOBeanRemote utilisateurDAO; public UtilisateurDAOBeanTest() super(); @Before public void setUp() throws Exception try securityClient = SecurityClientFactory.getSecurityClient(); Properties proprietes = new Properties(); proprietes.load(new FileInputStream("jndi.properties")); InitialContext ctx = new InitialContext(proprietes); utilisateurDAO = (UtilisateurDAOBeanRemote) ctx.lookup("VenteEnLigne/UtilisateurDAOBean/remote"); catch (Exception e) e.printStackTrace(); @Test public void testAjoutUtilisateurParClient()

System.out.println("Debut testAjoutUtilisateurParClient"); try // Jacques a le rôle Client securityClient.setSimple("Jacques", "JacquesMDP"); securityClient.login(); Client client = new Client(); // setters sur l’entite Client ... utilisateurDAO.sauver(client); catch (EJBAccessException ex) System.out.println("Erreur attendue de type EJBAccessException: " + ex.getMessage()); catch (Exception ex) ex.printStackTrace(); fail("Exception pendant le test testAjoutUtilisateurParClient"); System.out.println("Fin testAjoutUtilisateurParClient"); @Test public void testAjoutUtilisateurParGestionnaire()

System.out.println("Debut testAjoutUtilisateurParGestionnaire"); try // Robert a le rôle Gestionnaire securityClient.setSimple("Robert", "RobertMDP"); securityClient.login(); Client client = new Client(); // setters sur l’entite Client utilisateurDAO.sauver(client); catch (EJBAccessException ex) System.out.println("Erreur attendue de type

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 177: Ejb Jsf Struts Flex Jasper

EJBAccessException: " + ex.getMessage()); catch (Exception ex) ex.printStackTrace(); fail("Exception pendant le test testAjoutUtilisateurParGestionnaire"); System.out.println("Fin testAjoutUtilisateurParGestionnaire"); @Test public void testAjoutUtilisateurParAdministrateur()

System.out.println("Debut testAjoutUtilisateurParAdministrateur"); try // Irene a le rôle Administrateur securityClient.setSimple("Irene", "IreneMDP"); securityClient.login(); Client client = new Client(); BigDecimal fax = new BigDecimal("0143545654"); client.setFax(fax); BigDecimal telephone = new BigDecimal("0134345455"); client.setTelephone(telephone); utilisateurDAO.sauver(client); catch (EJBAccessException ex) System.out.println("Erreur attendue de type EJBAccessException: " + ex.getMessage()); catch (Exception ex) ex.printStackTrace(); fail("Exception pendant le test testAjoutUtilisateurParAdministrateur"); System.out.println("Fin testAjoutUtilisateurParAdministrateur"); @Test public void testAjoutUtilisateurParInconnu() System.out.println("Debut testAjoutUtilisateurParInconnu"); try // toto n’a aucun rôle securityClient.setSimple("toto", "toto"); securityClient.login(); Client client = new Client(); utilisateurDAO.sauver(client); catch (EJBAccessException ex) System.out.println("Erreur attendue de type EJBAccessException: " + ex.getMessage()); catch (Exception ex) ex.printStackTrace(); fail("Exception pendant le test testAjoutUtilisateurParInconnu"); System.out.println("Fin testAjoutUtilisateurParInconnu");

Cette classe JUnit lance plusieurs scénarios de test qui correspondent à l’ajout d’un utilisateur par des utilisateurs dont les rôles sont respectivement C (client), G (gestionnaire) et A (administrateur). Les exceptions possibles sont catchées et l’exécution des tests dans Eclipse génère les résultats attendus au niveau des logs de la console :

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 178: Ejb Jsf Struts Flex Jasper

Debut testAjoutUtilisateurParClient Erreur attendue de type EJBAccessException: Caller unauthorized Fin testAjoutUtilisateurParClient Debut testAjoutUtilisateurParGestionnaire Erreur attendue de type EJBAccessException: Caller unauthorized Fin testAjoutUtilisateurParGestionnaire Debut testAjoutUtilisateurParAdministrateur Fin testAjoutUtilisateurParAdministrateur Debut testAjoutUtilisateurParInconnu Erreur attendue de type EJBAccessException: Invalid User Fin testAjoutUtilisateurParInconnu

Les tests avec les utilisateurs Jacques (rôle Client) et Robert (Gestionnaire) lèvent une exception de type EJBAccessException: Caller unauthorized qui montre bien que ces utilisateurs n’ont pas le droit d’exécuter la méthode sauver(Utilisateur utilisateur) du stateful session bean . En revanche aucune exception n’est levée si l’utilisateur a le rôle Administrateur (cas d’Irene). La méthode est appelée et exécutée.

Enfin, un test supplémentaire peut être exécuté avec un utilisateur non défini dans le fichier venteEnLigne­users.properties. Ce test lève logiquement une exception pour signaler que l’utilisateur est inconnu, l’authentication ayant échoué :

Erreur attendue de type EJBAccessException: Invalid User

2. Gestion des rôles définis dans la base de données

JBoss permet d’authentifier un utilisateur via la base de données. La table Utilisateur contient une colonne TYPE_UTIL qui détermine le rôle d’un utilisateur. Elle contient également les login (colonne LOGIN) et les mots de passe (colonne PASSWORD).

Le module DatabaseServerLoginModule basé sur JDBC permet de sélectionner les login/mot de passe et les rôles en base.

Modifiez les fichiers suivants pour mettre en place l’authentification via la base de données.

1 ­ Fichier server\default\conf\login­config.xml :

<?xml version=’1.0’?> <!-- The XML based JAAS login configuration read by the org.jboss.security.auth.login.XMLLoginConfig mbean. Add an application-policy element for each security domain. --> ... <application-policy name="venteEnLigne_domaine_DB"> <authentication> <login- modulecode="org.jboss.security.auth.spi.DatabaseServerLoginModule"

flag="required"> <module-option name="dsJndiName"> java:/OracleDS </module-option> <module-option name="principalsQuery"> SELECT PASSWORD FROM

UTILISATEUR WHERE LOGIN=? </module-option> <module-option name="rolesQuery"> SELECT TYPE_UTIL, ’Roles’ FROM

UTILISATEUR WHERE LOGIN=? </module-option> </login-module> </authentication> </application-policy> </policy>

Le nom JNDI, OracleDS, de la source de données est le même que celui définit dans le fichier server/default/deploy/oracle­ds.xml.

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 179: Ejb Jsf Struts Flex Jasper

L’option principalsQuery définit la requête qui est exécutée par le module de login lors de l’authentification du Principal.

L’option rolesQuery définit la requête qui est exécutée par le module de login lors de la phase d’autorisation du Principal, afin de récupérer le(s) rôle(s).

2 ­ Fichier /VenteEnLigneClient/appClientModule/META­INF/jboss.xml :

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_5_0.dtd"> <jboss> <security-domain>java:/jaas/venteEnLigne_domaine_DB</security-domain> </jboss>

3 ­ Pour le test, il suffit de modifier le domaine de sécurité déclaré dans le stateless session bean UtilisateurDAOBean.java :

package com.eni.dvtejb.metier.sessions; ... @SecurityDomain("venteEnLigne_domaine_DB") @RolesAllowed("A", "G") @Stateful public class UtilisateurDAOBean implements UtilisateurDAOBeanRemote ...

En reprenant le test JUnit précédent, on constate que seul l’administrateur a le droit de créer un nouvel utilisateur. Ce résultat est conforme au résultat attendu.

3. Gestion des rôles à partir d’un annuaire LDAP

LDAP (Lightweight Directory Access Protocol) est une façon de communiquer avec un annuaire. L’annuaire étant une base de données hiérarchique ­ appelée également arbre ou DIT (Directory Information Tree) ­ constituée d’entrées représentées sous forme de branches et qui peut être construit selon un modèle organisationnel d’entreprise avec les divisions, les agences, les managers, les collaborateurs, etc.

JBoss contient également des modules de login, LdapLoginModule et LDAPExtLoginModule, pour accéder à des annuaires de type LDAP. Le module LdapLoginModule est adapté pour des arbres LDAP simples. Le module LDAPExtLoginModule est plus flexible : il convient pour des arbres LDAP simples et complexes.

La première étape pour l’intégration d’un annuaire LDAP dans JBoss est de modifier le fichier login­config.xml pour y ajouter l’application­policy adéquate :

<application-policy name="venteEnLigne_domaine_LDAP"> <authentication> <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" >

<module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</modul e-option> <module-option name="java.naming.provider.url">ldap://localhost:10389</module-option> <module-option name="java.naming.security.authentication">simple</module-option> <module-option name="bindDN">uid=admin,ou=system</module-option> <module-option name="bindCredential">secret</module-option> <module-option name="baseCtxDN">ou=users,ou=system</module-option> <module-option name="baseFilter">(uid=0)</module-option> <module-option name="rolesCtxDN">ou=groups,ou=system</module-option> <module-option name="roleFilter">(uniqueMember=1)</module-option> <module-option name="searchScope">ONELEVEL_SCOPE</module-option> <module-option name="allowEmptyPasswords">false</module-option> <module-option name="roleAttributeID">cn</module-option> </login-module> </authentication> </application-policy>

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 180: Ejb Jsf Struts Flex Jasper

Le login (uid=admin,ou=system) et le mot de passe (secret) indiqués dans le fichier sont ceux de l’administrateur du serveur ApacheDS. Une fois que l’administrateur est authentifié au serveur LDAP, l’utilisateur est recherché dans l’annuaire et son mot de passe est vérifié.

L’option roleAttributeID indique l’ID qui est utilisé dans l’annuaire pour assigner un rôle à un utilisateur.

Le fichier VenteEnLigne.LDIF suivant est au format LDIF (LDAP Data Interexchange Format). C’est un format standard pour lire et écrire des données dans un annuaire LDAP, à travers des exports et des imports de ces données, et surtout pour répliquer des données entre annuaires. Ce fichier définit un annuaire composé de quatre utilisateurs et de trois rôles.

VenteEnLigne.LDIF :

# Utilisateur: log1 dn: uid=log1,ou=users,ou=system cn: Jean Azerty sn: Azerty givenname: Jean objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson ou: Ressources Humaines ou: Personnes l: Versailles uid: log1 mail: [email protected] telephonenumber: +1 43 56 33 33 facsimiletelephonenumber: +1 43 51 33 33 roomnumber: 1233 userPassword: pwd1 # Utilisateur: log2 dn: uid=log2,ou=users,ou=system cn: Jeanne Cleter sn: Cleter givenname: Jeanne objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson ou: Ressources Humaines ou: Personnes l: Tours uid: log2 mail: [email protected] telephonenumber: +1 41 56 33 33 facsimiletelephonenumber: +1 40 51 33 33 roomnumber: 4613 userPassword: pwd2 # Utilisateur: log6 dn: uid=log6,ou=users,ou=system cn: Bob qwerty sn: qwerty givenname: Bob objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson ou: Ressources Humaines ou: Personnes l: Bordeaux uid: log6 mail: [email protected] telephonenumber: +1 42 56 73 33 facsimiletelephonenumber: +1 48 56 33 33 roomnumber: 6592 userPassword: pwd6

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 181: Ejb Jsf Struts Flex Jasper

# Utilisateur: log7 dn: uid=log7,ou=users,ou=system cn: Marc Doter sn: Doter givenname: Bob objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson ou: Ressources Humaines ou: Personnes l: Clamart uid: log7 mail: [email protected] telephonenumber: +1 42 56 73 33 facsimiletelephonenumber: +1 48 56 33 33 roomnumber: 6592 userPassword: pwd7 # Groupe: administrateur A dn: cn=A,ou=groups,ou=system objectClass: groupOfUniqueNames uniqueMember: uid=log6,ou=users,ou=system cn: A # Groupe: Gestionnaire G dn: cn=G,ou=groups,ou=system objectClass: groupOfUniqueNames uniqueMember: uid=log7,ou=users,ou=system cn: G # Groupe: client C dn: cn=C,ou=groups,ou=system objectClass: groupOfUniqueNames uniqueMember: uid=log1,ou=users,ou=system uniqueMember: uid=log2,ou=users,ou=system cn: C

Dans ce fichier LDIF, les DN (Distinguished Name) des utilisateurs sont constitués des UID (User Identifier) et des OU (Organizational Unit). L’utilisateur log6 a le rôle A. L’utilisateur log7 a le rôle G. Les utilisateurs log1 et log2 ont le rôle C.

Le tableau suivant fournit les significations de la plupart des attributs LDAP présents dans le fichier :

L’installation et le démarrage du serveur LDAP open­source Apache Directory Server qui est embarqué dans le browser LDAP Apache Directory Studio sont décrits dans le chapitre Mise en place de l’environnement. À l’aide de Apache Directory Studio, importez ce fichier LDIF dans le serveur LDAP Apache DS. Pour cela faites un clic droit sur la connexion en cours, sélectionnez Import puis Import LDIF et choisissez le fichier LDIF à importer :

Attribut Signification

dn Distinguished Name. C’est un moyen d’identification unique d’un objet dans l’arborescence.

cn Common Name : le nom commun de l’objet.

sn Surname : le nom de l’utilisateur.

givenname Prénom : le prénom de l’utilisateur.

objectclass Nom de la classe objet.

ou Organisation Unit : nom de l’unité organisationnelle.

l Localization : localité.

uid User Identifier : login.

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 182: Ejb Jsf Struts Flex Jasper

Une fois l’import terminé, la structure de l’annuaire apparaît dans le navigateur LDAP :

Un répertoire LDAP stocke ses informations dans des nœuds et génère une structure sous la forme d’un arbre. Chaque utilisateur possède un nœud qui stocke toutes ses informations et chaque groupe possède un nœud qui stocke les informations relatives aux utilisateurs qui font partie de ce groupe. Dans ce cas, un groupe est vu comme un rôle. Voici l’arbre LDAP créé par l’importation du fichier LDIF :

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 183: Ejb Jsf Struts Flex Jasper

Le schéma suivant représente l’arbre du fichier LDIF :

Modifiez le domaine de sécurité déclaré dans le stateless session bean UtilisateurDAOBean.java. La nouvelle valeur est venteEnLigne_domaine_LDAP.

Enfin mettez à jour le fichier /VenteEnLigneClient/appClientModule/META­INF/jboss.xml pour y inclure ce nouveau domaine de sécurité.

Si vous exécutez une fois de plus la classe de tests JUnit développée dans le paragraphe 9.2.1, vous vous apercevez que les résultats de la séquence de tests sont identiques.

La consultation des logs JBoss dans le fichier \jboss­6.0.0.M1\server\default\log\server.log permet de voir que l’utilisateur log6 est connecté au serveur LDAP et que son rôle est récupéré puis propagé :

getAppConfigurationEntry(venteEnLigne_domaine_LDAP), authInfo=AppConfigurationEntry[]:

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 184: Ejb Jsf Struts Flex Jasper

[0] LoginModule Class: org.jboss.security.auth.spi.LdapExtLoginModule ControlFlag: LoginModuleControlFlag : required Options: name=baseFilter, value=(uid=0) name=java.naming.security.authentication, value=simple name=java.naming.factory.initial, value=com.sun.jndi.ldap.LdapCtxFactory name=allowEmptyPasswords, value=false name=roleFilter, value=(uniqueMember=1) name=bindCredential, value=**** name=bindDN, value=uid=admin,ou=system name=java.naming.provider.url, value=ldap://localhost:10389 name=rolesCtxDN, value=ou=groups,ou=system name=baseCtxDN, value=ou=users,ou=system name=searchScope, value=ONELEVEL_SCOPE name=roleAttributeID, value=cn 2010-01-01 20:19:52,366 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) initialize 2010-01-01 20:19:52,366 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) Security domain: venteEnLigne_domaine_LDAP 2010-01-01 20:19:52,366 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) login 2010-01-01 20:19:52,366 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) Logging into LDAP server, env=java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, searchScope=ONELEVEL_SCOPE, java.naming.security.principal=uid=admin,ou=system, baseCtxDN=ou=users,ou=system, roleAttributeID=cn, roleFilter=(uniqueMember=1), allowEmptyPasswords=false, rolesCtxDN=ou=groups,ou=system, baseFilter=(uid=0), jboss.security.security_domain=venteEnLigne_domaine_LDAP, java.naming.provider.url=ldap://localhost:10389, bindDN=uid=admin,ou=system, java.naming.security.authentication=simple, bindCredential=secret, java.naming.security.credentials=*** 2010-01-01 20:19:52,372 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) Logging into LDAP server, env=java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, searchScope=ONELEVEL_SCOPE, java.naming.security.principal=uid=log6,ou=users,ou=system, baseCtxDN=ou=users,ou=system, roleAttributeID=cn, roleFilter=(uniqueMember=1), allowEmptyPasswords=false, rolesCtxDN=ou=groups,ou=system, baseFilter=(uid=0), jboss.security.security_domain=venteEnLigne_domaine_LDAP, java.naming.provider.url=ldap://localhost:10389, bindDN=uid=admin,ou=system, java.naming.security.authentication=simple, bindCredential=secret, java.naming.security.credentials=*** 2010-01-01 20:19:52,385 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) Assign user to role A

2010-01-01 20:19:52,385 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) User ’log6’ authenticated, loginOk=true

2010-01-01 20:19:52,385 TRACE [org.jboss.security.auth.spi.LdapExtLoginModule] (WorkerThread#0[127.0.0.1:53372]) commit, loginOk=true 2010-01-01 20:19:52,385 TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.venteEnLigne_doma ine_LDAP] (WorkerThread#0[127.0.0.1:53372]) defaultLogin, lc=javax.security.auth.login.LoginContext@1388589, subject=Subject(29822465).principals=org.jboss.security.SimplePrincipal@21 582335(log6)org.jboss.security.SimpleGroup@8397980(Roles(members:A)) 2010-01-01 20:19:52,385 TRACE

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 185: Ejb Jsf Struts Flex Jasper

[org.jboss.security.plugins.auth.JaasSecurityManagerBase.venteEnLigne_doma ine_LDAP] (WorkerThread#0[127.0.0.1:53372]) updateCache, inputSubject=Subject(29822465).principals=org.jboss.security.SimplePrincip al@21582335(log6)org.jboss.security.SimpleGroup@8397980(Roles(members:A)), cacheSubject=Subject(23619492).principals=org.jboss.security.SimplePrincip al@21582335(log6)org.jboss.security.SimpleGroup@8397980(Roles(members:A)) 2010-01-01 20:19:52,386 TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.venteEnLigne_doma ine_LDAP] (WorkerThread#0[127.0.0.1:53372]) Inserted cache info: org.jboss.security.plugins.auth.JaasSecurityManagerBase$DomainInfo@16c4f11 [Subject(23619492).principals=org.jboss.security.SimplePrincipal@21582335 (log6)org.jboss.security.SimpleGroup@8397980(Roles(members:A)),credential.c lass=java.lang.String@2707298,expirationTime=1262375369795] 2010-01-01 20:19:52,386 TRACE [org.jboss.security.plugins.auth.JaasSecurityManagerBase.venteEnLigne_doma ine_LDAP] (WorkerThread#0[127.0.0.1:53372]) End isValid, true 2010-01-01 20:19:52,389 TRACE [org.jboss.security.plugins.authorization.JBossAuthorizationContext] (WorkerThread#0[127.0.0.1:53372]) Control flag for entry:org.jboss.security.authorization.config.AuthorizationModuleEntryorg .jboss.security.authorization.modules.DelegatingAuthorizationModule:REQU IREDis:[REQUIRED] 2010-01-01 20:19:52,389 TRACE [org.jboss.security.authorization.modules.ejb.EJBPolicyModuleDelegate] (WorkerThread#0[127.0.0.1:53372]) method=public void com.eni.dvtejb.metier.sessions.UtilisateurDAOBean.sauver(com.eni.dvtejb.me tier.entities.Utilisateur), interface=Remote, requiredRoles=Roles(A,) 2010-01-01 20:19:52,392 INFO [com.eni.dvtejb.metier.sessions.UtilisateurDAOBean] (WorkerThread#0[127.0.0.1:53372]) Debut methode sauver()

Comme le montre les logs, l’utilisateur log6 a le rôle A. C’est le rôle qui a le droit d’exécuter la méthode void sauver(Utilisateur utilisateur).

4. Gestion des rôles via l’interface EJBContext

La gestion des rôles peut également être gérée de façon plus programmatique avec l’utilisation de l’API Java Security et de l’interface javax.ejb.EJBContext. Cette interface donne accès au contexte d’exécution d’une instance de bean. En particulier deux méthodes fournissent au développeur des informations de sécurité concernant le client du bean :

java.security.Principal getCallerPrincipal() ; boolean isCallerInRole (java.lang.String roleName) ;

À partir du contexte de la session (l’interface SessionContext hérite de l’interface EJBContext), il est possible de récupérer le Principal Name c’est­à­dire le nom du client du bean en passant par la méthode getName() de l’interface java.security.Principal qui est retournée par la méthode getCallerPrincipal().

Le rôle du client peut être contrôlé avec la méthode isCallerInRole(String role). Elle renvoie le boolean true si le client possède le rôle passé en paramètre.

Au niveau du bean, le contexte de la session peut être injecté à l’aide de l’annotation @Resource.

@DeclareRoles( "A", "G") @Stateful public class UtilisateurDAOBean implements UtilisateurDAOBeanRemote @Resource private SessionContext sctx; public void sauver(Utilisateur utilisateur) Principal principal = sctx.getCallerPrincipal(); String appelant = principal.getName(); // Contrôle sur le nom. Est-ce l’utilisateur log6 ? if ("log6".equals(appelant)) ...

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 186: Ejb Jsf Struts Flex Jasper

// Contrôle sur le rôle. Est-ce un Administrateur ? if (sctx.isCallerInRole("A")) ...

Au niveau organisationnel, la déclaration des rôles et des permissions, la configuration du domaine de sécurité et du module d’authentification sont sous la responsabilité du déployeur.

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnB3jFdlH82ICwA=-enidentnumber

Page 187: Ejb Jsf Struts Flex Jasper

Présentation de Struts 2

Apache Struts est un framework Open Source pour développer des applications web Java EE. Struts 1.0 est né en juin 2001. L’auteur principal de cette première version est Craig R. McClanahan. En juin 2003, c’est au tour de la version 1.1 de voir le jour. L’architecture du noyau de Struts 2, apparue en 2006, est basée sur celles de Struts 1 et de WebWork / XWork qui est un des composants du projet Open Source OpenSymphony (les autres composants sont notamment Quartz, OGNL, SiteMesh) dont le développeur principal est Jason Carreira.

Struts 2 inclus différentes technologies (Freemarker, Javascript, Java, CSS, HTML, OGNL et XML entre autres) dont certaines sont intégrées sous la forme de plugins.

Au niveau des différences avec Struts 1, on peut constater que, par défaut, Struts 2 recherche des URLs qui se terminent par .action alors que l’extension par défaut des URLS dans Struts 1 est .do. Les form beans (ActionForms) pour capturer les données d’une page sont remplacés par les propriétés des actions.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MksHpWllH82ICwA=-enidentnumber

Page 188: Ejb Jsf Struts Flex Jasper

Le modèle MVC 2

Struts repose sur une architecture MVC (Modèle­Vue­Contrôleur) et plus précisément sur le modèle 2 de cette architecture, aussi appelée MVC 2 :

La couche Modèle représente la partie métier (bean), la couche Vue représente la partie présentation (JSP) et la couche Contrôleur effectue les contrôles et les redirections.

Il existe deux modèles d’architecture MVC : le modèle 1 (MVC 1) et le modèle 2 (MVC 2).

Dans MVC 1, le contrôleur de l’application est décentralisé dans la mesure où la navigation est déterminée à partir de la page actuelle.

Dans MVC 2, une servlet intercepte les requêtes pour déterminer la navigation.

Struts est un bon exemple de framework basé sur MVC 2 car la servlet StrutsPrepareAndExecuteFilter sélectionne la vue à retourner en réponse à une requête de l’utilisateur. Dans Struts 1, la servlet était ActionServlet. Un élément central de Struts 2 est le concept d’action. Une action est une classe qui est en charge de traiter des formulaires HTML et des requêtes.

Lorsqu’un client émet une requête, celle­ci est interceptée par le contrôleur représenté par la servlet StrutsPrepareAndExecuteFilter. Cette servlet invoque l’action du modèle en fonction de la configuration définie dans le fichier struts.xml ou suivant les annotations ou par convention. L’action effectue ensuite un traitement qui fait appel aux EJB, puis elle retourne un Result à la vue. Le schéma suivant illustre l’implémentation du modèle MVC 2 dans Struts 2 et l’utilisation des EJB :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mg42R3JlH82ICwA=-enidentnumber

Page 189: Ejb Jsf Struts Flex Jasper

Appels des EJB 3 dans Struts 2

L’application Web doit être construite de manière à utiliser les EJB : les actions doivent gérer la communication avec les EJB pour la partie métier. La création d’un projet Struts 2 passe par la création d’un projet Web dynamique spécifique à l’utilisation de Struts 2. Appelez ce projet VenteEnLigneWebStruts2 par exemple. Veillez à cocher la case Add project to an EAR puisque ce projet fait partie de l’application.

Téléchargez la librairie Struts sur le site d’Apache http://struts.apache.org/2.x/ et ajoutez les librairies qui se trouvent sous le répertoire /lib de cette distribution Struts 2 dans le chemin de compilation (build path) du projet. Il n’est pas nécessaire d’ajouter toutes les librairies qui sont présentes et, pour éviter des conflits, il est même recommandé d’en exclure certaines comme : struts2­spring­plugin­2.1.8.jar, struts2­osgi­demo­bundle­2.1.8.jar, struts2­plexus­plugin­2.1.8.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 190: Ejb Jsf Struts Flex Jasper

Les librairies indispensables sont les suivantes :

xwork­core­2.1.6.jar

struts2­core­2.1.8.jar (contient les classes principales du framework)

ognl­2.7.3.jar (pour les expressions OGNL)

freemarker­2.3.15.jar

struts2­dojo­plugin­2.1.8.jar

Il suffit de les inclure dans le répertoire server/default/lib de JBoss. Une alternative consiste à les copier dans le répertoire WEB­INF/lib du projet. Dans ce cas, elles seront incluses dans l’archive WAR générée.

Pour débuter avec Struts 2, vous pouvez installer les applications Web qui se trouvent dans le répertoire /apps de la distribution Struts 2, notamment l’application blank, pour démarrer un projet et l’application showcase qui

montre de nombreuses fonctionnalités disponibles dans le framework. Il suffit de les copier dans le répertoire /deploy de JBoss pour les déployer.

Modifiez le descripteur de déploiement WEB­INF/web.xml pour y déclarer le filtre struts2 qui est chargé d’intercepter les requêtes HTTP :

<?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_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>VenteEnLigneWebStruts2</display-name> <welcome-file-list>

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 191: Ejb Jsf Struts Flex Jasper

<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> <jsp-config> <taglib> <taglib-uri>/s</taglib-uri> <taglib-location>/WEB-INF/struts-tags.tld</taglib-location> </taglib> </jsp-config> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilte r </filter-class> <init-param> <param-name>actionPackages</param-name> <param-value>com.eni.dvtejb.clientStruts2.action</param-value> </init-param> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener- class>org.apache.struts2.tiles.StrutsTilesListener</listener-class> </listener> <session-config> <!-- Durée de vie de la session, en minutes --> <session-timeout>30</session-timeout> </session-config> </web-app>

Pour que les ressources statiques (images, css, JavaScript, etc.) ainsi que les servlets ne soient pas traitées par le filtre, vous devez changer le mapping du filtre dans le fichier web.xml. Par exemple, le mapping suivant traite toutes les requêtes avec l’extension *.action et toutes les requêtes avec le prefix /struts qui correspond au names pace défini dans le(s) fichier(s) de configuration Struts :

<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/struts/*</url-pattern> </filter-mapping>

Copiez le fichier struts­tags.tld qui se trouve dans le répertoire META­INF à l’intérieur de la librairie struts2­core­2.1.8.jar vers le répertoire /WEB­INF du projet VenteEnLigneWebStruts2.

Pour pouvoir utiliser les EJB, il faut inclure le projet VenteEnLigneEJB dans la liste des modules dont dépend ce projet Web.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 192: Ejb Jsf Struts Flex Jasper

1. Développement et configuration des actions pour l’authentification

Pour permettre de gérer les deux types d’utilisateurs possibles de l’application (nouvel utilisateur et utilisateur possédant déjà un compte), la page d’accueil propose deux liens : un lien vers une page d’authentification et un lien vers une page d’inscription si c’est la première fois que l’utilisateur visite le site.

La cinématique de départ de l’application est illustrée par le schéma suivant :

Les fonctionnalités peuvent être identifiées dans des fichiers de configuration XML à part, comme c’est le cas du fichier login.xml pour tout ce qui est en rapport avec l’identification et du fichier inscription.xml pour les actions relatives à l’inscription. Le tag <include> permet ensuite facilement l’inclusion de plusieurs fichiers XML dans le fichier principal struts.xml. Des fichiers de configuration sont inclus automatiquement et chargés au démarrage. C’est le cas de struts­default.xml qui est la configuration par défaut du framework et des fichiers struts­plugin.xml qui sont inclus dans les

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 193: Ejb Jsf Struts Flex Jasper

archives jar des plugins.

Les actions font appels à des EJB définis dans le projet VenteEnLigneEJB.

La page JSP qui propose au visiteur de se connecter ou de s’enregistrer est la suivante : Page index.jsp

<html> <head> <title>Page index</title> <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() + "/css/styles.css" %>"> </head> <body> <br><br><br><br><br> <center> <a href="activerLogin.action">Se connecter </a> | <a href="inscription.action">S’inscrire </a> </center> </body> </html>

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 194: Ejb Jsf Struts Flex Jasper

Le lien d’authentification Se connecter fait appel à l’action activerLogin pour afficher la page JSP d’authentification login/login.jsp. La taglib (bibliothèque de balises) utilisée est struts­tags. Elle est référencée dans la JSP par le préfixe s.

Page login.jsp

<%@ taglib prefix="s" uri="/struts-tags" %> <link href="<s:url value="/css/styles.css"/>" rel="stylesheet" type="text/css"/> <s:head theme="xhtml" /> <s:debug /> <center> <h2><s:text name="login.titre" /></h2> <s:form action="doLogin" method="POST" theme="xhtml" > <table class="table1"> <tr> <td colspan="2"> <s:text name="login.phrase"></s:text> </td> </tr> <tr> <td colspan="2"> <s:actionerror /> </td> </tr> <s:textfield name="login" key="login.login" /> <s:password name="password" key="login.pwd" /> <s:submit key="login.bouton" align="center"/> </table> </s:form> <br/> <br/> <br/> <s:url id="url" value="/index.jsp" /> <s:a href="%url" theme="xhtml"> <s:text name="login.retour" /> </s:a> </center>

Le formulaire comporte un champ de saisie <s:textfield> pour le login et un champ de saisie de mot de passe <s:password>.

Les actions sont utilisées pour répondre à des requêtes HTTP. Dans Struts 2, une action est un simple POJO, elle n’est pas obligée d’hériter d’une classe ou d’implémenter une interface. Cependant hériter de la classe com.opensymphony.xwork2.ActionSupport est une bonne idée car celle­ci fournit des implémentations par défaut concernant l’internationalisation, la validation, etc.

L’action com.eni.dvtejb.clientStruts2.action.LoginAction contient la logique métier qui permet d’authentifier un utilisateur. Elle implémente la méthode execute(). Celle­ci est automatiquement appelée lors de l’exécution de l’action. Elle retourne SUCCESS en cas de connexion réussie, ERROR sinon. Un lookup JNDI est utilisé pour récupérer une référence à l’interface distante FacadeRemote du stateless session bean FacadeBean. La méthode findUtilisateur(String login, String password) valide la connexion.

Avant d’exécuter l’action, Struts injecte les valeurs des champs du formulaire dans les propriétés de l’action. La classe implémente l’interface SessionAware qui lui permet d’accéder aux attributs de la session. L’interface SessionAware requiert l’implémentation de la méthode setSession(Map session).

Struts 2 utilise une Map session au lieu d’un objet de type HTTPSession.

LoginAction.java :

package com.eni.dvtejb.clientStruts2.action; import java.util.Map; import javax.naming.InitialContext; import org.apache.struts2.interceptor.SessionAware; import org.jboss.logging.Logger; import org.jboss.web.tomcat.security.login.WebAuthentication;

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 195: Ejb Jsf Struts Flex Jasper

import com.eni.dvtejb.metier.entities.Administrateur; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Gestionnaire; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.sessions.FacadeRemote; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator; public class LoginAction extends ActionSupport implements SessionAware private static final Logger log = Logger.getLogger(LoginAction.class); private Map session; private Client client; public Client getClient() return client; public void setClient(Client client) this.client = client; private static final long serialVersionUID = 1L; public String changerLangue()throws Exception log.info("Entrée dans la méthode changerLangue()"); return INPUT; public String execute() throws Exception log.info("Entrée dans la méthode execute() "); InitialContext initialContext = new InitialContext(); FacadeRemote facade = (FacadeRemote) initialContext.lookup("VenteEnLigne/FacadeBean/remote"); Utilisateur utilisateur = facade.findUtilisateurByLoginPwd(getLogin(), getPassword()); WebAuthentication pwl = new WebAuthentication(); pwl.login(getLogin(), getPassword()); if (utilisateur instanceof Client) log.info("=========> L’utilisateur est un client ! "); client = (Client)utilisateur; else if (utilisateur instanceof Administrateur) log.info("=========> L’utilisateur est un Administrateur ! "); return "administration"; else if (utilisateur instanceof Gestionnaire) log.info("=========> L’utilisateur est un Gestionnaire ! "); return "administration"; if(null == client) addActionError("Mauvais login et/ou mauvais mot de passe, veuillez réessayer."); return ERROR; else // Une alternative à l’interface SessionAware //Map session = ActionContext.getContext().getSession(); // La récupération de l’adresse est possible grâce à l’utilisation d’un FETCH de type EAGER dans l’entity bean

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 196: Ejb Jsf Struts Flex Jasper

Utilisateur.java : // @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) Adresse adresse = client.getAdresseFk(); log.info("code postal du client : " + adresse.getCodepostal()); log.info("LoginAction - client.getUtilisateurid() : " + client.getUtilisateurid()); session.put("leclient", client); session.put("ladresse", adresse); return SUCCESS; public void setSession(Map session) this.session = session; public Map getSession() return session; private String login = null; @RequiredStringValidator(message="Le login est obligatoire !!", trim=true) public String getLogin() return login; public void setLogin(String login) this.login = login; private String password = null; @RequiredStringValidator(message="Le password est obligatoire !", trim=true) public String getPassword() return password; public void setPassword(String password) this.password = password;

Dans le cas d’un utilisateur de type client, les entity beans client et adresse sont mis en session. Les méthodes getLogin() et getPassword() portent l’annotation @RequiredStringValidator pour indiquer que les propriétés login et password sont obligatoires. L’attribut trim permet d’indiquer que les espaces en fin de chaîne doivent être supprimés.

La prise en charge de l’authentification par JAAS nécessite l’ajout du fichier jboss­web.xml dans le répertoire /WEB­INF pour spécifier le domaine de sécurité. Ce domaine est ensuite utilisé par la méthode login de la classe org.jboss.web.tomcat.security.login.WebAuthentication :

<?xml version=’1.0’ encoding=’UTF-8’ ?> <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3V2//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd"> <jboss-web> <security-domain>java:/jaas/venteEnLigne_domaine</security-domain> </jboss-web>

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 197: Ejb Jsf Struts Flex Jasper

Si l’utilisateur ne remplit pas le login ou/et le password, des messages d’erreur s’affichent en rouge pour signaler les champs obligatoires ou de mauvais login/password grâce à l’attribut message de l’annotation @RequiredStringValidator.

La méthode addActionError (String unMessageErreur) permet d’afficher des messages d’erreur sur des traitements particuliers, par exemple lorsque l’utilisateur saisit un mauvais login/mot de passe :

En cas de connexion réussie, deux cas se présentent :

L’utilisateur est de type Client. Dans ce cas il est dirigé vers la page menu/accueil.jsp.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 198: Ejb Jsf Struts Flex Jasper

L’utilisateur est de type Administrateur ou Gestionnaire. Alors il est dirigé vers la page d’administration/gestion du site, à savoir administration/administration.jsp. Cette partie de l’application est surtout là pour mettre en avant les possibilités de gestion des droits des utilisateurs.

Page accueil.jsp

<%@ taglib prefix="s" uri="/struts-tags" %> <html> <body> <div align="right" > <img src="<%=request.getContextPath()%>/images/utilisateur.gif" ><br> <s:text name="general.nom" /> : <s:property value="#session.leclient.nom" /><br> <s:text name="general.prenom" /> : <s:property value="#session.leclient.prenom" /> </div> <s:debug/> <center> <img src="<%=request.getContextPath()%>/images/ INGDMYFS0341_petite.png"><br /><br /> <h1> <s:text name="accueil.bienvenue" > <s:param ><s:property value="#session.leclient.nom" /> </s:param> <s:param ><s:property value="#session.leclient.prenom" /> </s:param> </s:text> </h1> <s:text name="accueil.phrase1" /> : <!-- <s:property value="login" /><br> --> <s:property value="#session.leclient.login" /><br> <!-- %edit est évalué par rapport à l’URL définie dans le tag s:url --> <s:url id="edit" action="editionProfil"/><s:a href="%edit" ><s:property value="getText(’accueil.lienProfil’)"/></s:a> - <s:url id="affiche" action="afficheMagasin"/><s:a href="%affiche"><s:text name="accueil.lienListeArticles" /></s:a> - <s:url id="historique" action="historiquecommandes"/><s:a href="%historique"><s:text name="accueil.lienHistorique" /></s:a> - <s:url id="recherche" action="recherche" /><s:a href="%recherche" ><s:text name="accueil.lienRecherche" /><br> </s:a> </center> </body> </html>

Le tag <s:url> est une facilité supplémentaire introduite dans Struts 2 pour générer des URLs :

<s:url id="edit" action="editionProfil"/> génère une URL du type <namespace>/NomAction c’est­à­dire /VenteEnLigneWebStruts2/editionProfil.action.

Après une connexion réussie, l’utilisateur de type Client est dirigé vers l’écran d’accueil :

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 199: Ejb Jsf Struts Flex Jasper

Fichier de configuration struts.xml :

Il correspond au fichier struts­config.xml de la version 1 de Struts et doit se trouver dans le classpath. Pour des applications complexes, il vaut mieux diviser la configuration en plusieurs packages. Dans Struts 2.0.x, le package par défaut est struts­default. Le fichier struts.xml inclut les différents fichiers XML de configuration :

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <include file="login.xml" /> <include file="accueil.xml" /> <include file="magasin.xml" /> <include file="inscription.xml" /> <constant name="struts.convention.action.packages" value="com.eni.dvtejb.clientStruts2.action" /> </struts>

Le fichier de configuration src/login.xml qui décrit les règles de navigation pour les premiers écrans est le suivant :

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="global-messages" /> <package name="login" extends="struts-default"> <!-- DEBUT TILES --> <result-types> <!-- Defini le type de résultat qui renvoie vers une definition Tiles --> <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult" /> </result-types> <!-- FIN TILES --> <!-- DEBUT INTERCEPTORS -->

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 200: Ejb Jsf Struts Flex Jasper

<interceptors> <interceptor name="autorisation" class="com.eni.dvtejb.clientStruts2.interceptor.AutorisationInterceptor" /> <interceptor-stack name="unepile"> <interceptor-ref name="exception" /> <interceptor-ref name="i18n" /> <interceptor-ref name="chain" /> <interceptor-ref name="params" /> <interceptor-ref name="autorisation" /> </interceptor-stack> </interceptors> <!-- FIN INTERCEPTORS --> <!-- DEBUT LOGIN --> <action name="changerLangue" class="com.eni.dvtejb.clientStruts2.action.LoginAction" method="changerLangue"> <result name="input">/index.jsp</result> </action> <action name="activerLogin" > <result>/login/login.jsp</result> <!--<result type="tiles">VenteEnLigne.login</result>--> </action> <action name="doLogin" class="com.eni.dvtejb.clientStruts2.action.LoginAction"> <result name="input">/login/login.jsp</result> <result name="error">/login/login.jsp</result> <result type="tiles" name="administration">VenteEnLigne.administration</result> <result type="tiles">VenteEnLigne.accueil</result> </action> <!-- FIN LOGIN --> </package> </struts>

Les types de result (résultat) présents à l’intérieur de ce fichier correspondent aux valeurs retournées par les actions. Ces valeurs sont de type String. Les valeurs prédéfinies des results sont définies à l’intérieur de l’interface com.opensymphony.xwork2.Action :

SUCCESS : l’action a été exécutée avec succès.

NONE : l’action a été exécutée avec succès mais ne renvoie pas une vue.

ERROR : l’action ne s’est pas exécutée correctement.

INPUT : les données en entrée de l’action ne sont pas correctes ou sont insuffisantes.

LOGIN : l’action ne s’est pas exécutée correctement car l’utilisateur n’est pas connecté.

2. Configuration du framework Tiles 2

Tiles est un framework utilisé en complément de Struts pour le développement d’interfaces utilisateur. Le développeur assemble des tiles (des carreaux qui peuvent être des pages JSP, HTML…) et réutilise des templates (en français modèle ou encore gabarit). Les templates permettent d’éviter de dupliquer du code de mise en page et de minimiser l’effort de reprise d’une modification. En effet lorsque la mise en page du template est modifiée, l’ensemble des pages

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 201: Ejb Jsf Struts Flex Jasper

basées sur ce template est mis à jour.

Voici un exemple de template :

L’en­tête, le pied de page et le menu à gauche ne sont développés qu’une seule fois. Ce sont des éléments communs à toutes les pages. Seul le corps est dynamique et évolue en fonction de la navigation.

Pour installer Tiles, copiez vers le répertoire server/default/lib de JBoss les librairies suivantes et qui se trouvent dans le répertoire /lib de la distribution struts­2.1.8 :

struts2­tiles­plugin­2.1.8.jar (le plugin Tiles fourni avec Struts 2),

tiles­api­2.0.6.jar (API de manipulation),

tiles­core­2.0.6.jar (moteur de Tiles),

tiles­jsp­2.0.6 (support des JSP).

Ajoutez également les librairies dont dépend Tiles : commons­beanutils­1.7.0.jar et commons­digester­2.0.jar. Vous pouvez les récupérer dans le répertoire /lib de la distribution tiles­2.0.6­bin sur le site d’Apache à l’adresse http://tiles.apache.org/download.htm. Elles sont également disponibles dans le répertoire /lib de la distribution struts­2.1.8.

Enfin, copiez le fichier tiles­jsp.tld qui se trouve à l’intérieur de la librairie tiles­jsp­2.0.6.jar (répertoire META­INF/tld) vers le répertoire WEB­INF du projet.

Déclarez le listener StrutsTilesListener dans WEB­INF/web.xml. Ce listener fait partie de la librairie struts2­tiles­plugin­2.1.8.jar.

Voici le contenu du descripteur de déploiement 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_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>VenteEnLigneWebStruts2</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file>

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 202: Ejb Jsf Struts Flex Jasper

<welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <jsp-config> <taglib> <taglib-uri>/s</taglib-uri> <taglib-location>/WEB-INF/struts-tags.tld</taglib-location> </taglib> </jsp-config> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilte r </filter-class> <init-param> <param-name>actionPackages</param-name> <param- value>com.eni.dvtejb.clientStruts2.action</param-value> </init-param> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener- class>org.apache.struts2.tiles.StrutsTilesListener</listener-class> </listener> <session-config> <!-- Durée de vie de la session, en minutes --> <session-timeout>30</session-timeout> </session-config> </web-app>

Le fichier WEB­INF/tiles.xml contient les définitions de templates qui sont utilisées dans l’application. C’est donc dans ce fichier qu’il faut ajouter au fur et à mesure du développement des pages les définitions des templates. Ces templates héritent de la template TemplateStandard afin de réutiliser le header, le footer et le menu latéral.

Voici le contenu partiel du fichier tiles.xml :

<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN" "http://tiles.apache.org/dtds/tiles-config_2_0.dtd"> <tiles-definitions> <definition name="TemplateStandard" template="/tiles/classic.jsp" >

<put-attribute name="header" value="/tiles/header.jsp" /> <put-attribute name="menu" value="/tiles/menu.jsp" /> <put-attribute name="body" value="/tiles/body.jsp" /> <put-attribute name="footer" value="/tiles/footer.jsp" /> </definition> <definition name="TemplateAdministration" template="/tiles/classic.jsp" > <put-attribute name="header" value="/tiles/header.jsp" /> <put-attribute name="menu" value="/tiles/menuAdmin.jsp" /> <put-attribute name="body" value="/tiles/body.jsp" /> <put-attribute name="footer" value="/tiles/footer.jsp" /> </definition> <definition name="VenteEnLigne.login" extends="TemplateStandard"> <put-attribute name="title" value="Connexion" />

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 203: Ejb Jsf Struts Flex Jasper

<put-attribute name="body" value="/login/login.jsp" /> </definition> <definition name="VenteEnLigne.accueil" extends="TemplateStandard"> <put-attribute name="title" value="Accueil" /> <put-attribute name="body" value="/menu/accueil.jsp" /> </definition> ... </tiles-definitions>

Voici le code du template tiles/classic.jsp qui permet d’organiser le header, le menu de gauche, le footer et le contenu de droite :

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <html> <head> <title><tiles:getAsString name="title"/></title> </head> <body> <table width="100%" height="100%" border="1"> <tr height="5%"> <td colspan="2"> <center><tiles:insertAttribute name="header" /></center> </td> </tr> <tr height="90%"> <td width="80"> <tiles:insertAttribute name="menu" /> </td> <td> <tiles:insertAttribute name="body" /> </td> </tr> <tr height="5%"> <td colspan="2" > <center><tiles:insertAttribute name="footer" /></center> </td> </tr> </table> </body> </html>

La page header.jsp qui apparaît en haut de la page est la suivante :

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <%@ taglib prefix="s" uri="/struts-tags" %> <link href="<s:url value="/css/styles.css"/>" rel="stylesheet" type="text/css"/> <!-- En cas de fin de session, l’utilisateur est automatiquement redirigé vers la page principale --> <meta http-equiv="refresh" content="<%= session.getMaxInactiveInterval() %>;url=index.jsp" > <div style="width: 100%; height: 100%; background-color: #ABBAF3" > <strong><s:text name="header.titre" /></strong> </div>

La page footer.jsp apparaît en bas de page :

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <%@ taglib prefix="s" uri="/struts-tags" %> <link href="<s:url value="/css/styles.css"/>" rel="stylesheet" type="text/css"/> <div style="width: 100%; background-color: #ABBAF3" > <img src="images/logoJBoss.png"> <img src="images/tiles.png"> <strong><s:text name="footer.titre" />

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 204: Ejb Jsf Struts Flex Jasper

</strong><img src="images/struts2.png"> <img src="images/EJBS_petite.png"> </div>

Enfin, la page menu.jsp permet d’afficher le menu latéral gauche :

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <%@ taglib prefix="s" uri="/struts-tags" %> <div style="width: 100%; height: 100%; background-color: #ADDAA1" > <table> <tr> <td> <strong><s:text name="menu.titre" /></strong> </td> </tr> <tr> <td width="100" valign="top"> <s:url id="accueil" action="accueil" /> <s:a href="%accueil" ><strong><s:text name="menu.accueil" /><br> <img src="<%=request.getContextPath()%>/images/menu_accueil.png"> </s:a> </td> </tr> <tr> <td width="100" valign="top"> <s:url id="profil" action="editionProfil" /> <s:a href="%profil" ><strong><s:text name="menu.profil" /><br> <img src="<%=request.getContextPath()%>/images/portrait.jpg" height="50" width="50"> </s:a> </td> </tr> <tr> <td width="100" valign="top"> <s:url id="magasin" action="afficheMagasin" /> <s:a href="%magasin" ><strong><s:text name="menu.articles" /><br> <img src="<%=request.getContextPath()%>/images/etageres.jpg"> </s:a> </td> </tr> <tr> <td width="100" valign="top"> <s:url id="historique" action="historiquecommandes" /> <s:a href="%historique" ><s:text name="menu.historique" /><br> <img src="<%=request.getContextPath()%>/images/historique.gif"> </s:a> </td> </tr> <tr> <td width="100" valign="top"> <s:url id="recherche" action="recherche" /> <s:a href="%recherche" ><s:text name="menu.recherche" /><br> <img src="<%=request.getContextPath()%>/images/recherche.gif"> </s:a> </td> </tr> <tr> <td width="100" valign="top"> <s:url id="question" action="question" /> <s:a href="%question" ><s:text name="menu.contact" /><br> <img src="<%=request.getContextPath()%>/images/question.png"> </s:a> </td> </tr>

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 205: Ejb Jsf Struts Flex Jasper

<tr> <td width="100" valign="top"> <a href="<%=request.getContextPath()%>/login/logout.jsp"><s:text name="menu.deconnexion" /><br> <img src="<%=request.getContextPath()%>/images/deconnexion.gif" height="50" width="50"> </a> </td> </tr> </table> </div>

Concernant les liens, une alternative possible à l’utilisation de l’objet implicite request est l’expression EL (Expression Language) suivante :

<img src="$pageContext.request.contextPath/images/menu_accueil.png">

3. Thèmes

Struts 2 contient des thèmes graphiques prédéfinis qui déterminent la disposition des différents éléments d’une page. De façon générale, certaines propriétés peuvent être redéfinies par l’intermédiaire d’un fichier de propriétés struts.properties à placer à la racine du répertoire des sources. Ce fichier écrase les propriétés définies dans default.properties.

Par exemple, pour utiliser le thème simple, qui n’ajoute aucune modification au niveau du rendu des éléments d’une page, la ligne suivante est ajoutée dans le fichier /src/struts.properties.

struts.ui.theme=simple

Les autres thèmes prédéfinis sont xhtml (le thème par défaut), css_xhtml et ajax. Struts 2 offre également la possibilité de créer des thèmes.

4. Messages et internationalisation

Il est recommandé d’externaliser les libellés des champs dans des fichiers de ressources, également appelés fichiers de properties. Il suffit pour cela de créer un fichier, par exemple messages.properties, dans le répertoire /src et de le déclarer dans le fichier src/struts.properties de la façon suivante :

struts.custom.i18n.resources=messages struts.i18n.encoding=ISO-8859-1

Struts facilite également l’internationalisation des applications. L’utilisateur a la possibilité de choisir la langue de son choix. La langue de l’application dépend de la locale qui est configurée dans les paramètres du navigateur. Il est néanmoins possible de changer ce paramètre sans modifier les paramètres du navigateur grâce au paramètre request_locale. Ce paramètre est géré par l’intercepteur I18n qui fait partie de la pile d’intercepteurs présent par défaut dans struts­default.xml. L’intercepteur I18n se souvient au cours de la session de la locale sélectionnée par l’utilisateur.

Si dans le fichier struts.properties le paramètre struts.locale vaut fr alors la langue utilisée sera le français, même si le navigateur définit une autre langue comme langue par défaut. En revanche, si le fichier struts.properties ne contient pas le paramètre struts.locale alors la langue utilisée sera celle définit par la locale du navigateur.

La requête activerLogin.action?request_locale=fr met le paramètre request_locale à FR dans la session de l’utilisateur. La valeur de ce paramètre sera utilisée dans les prochaines requêtes. Struts utilise dans ce cas le fichier de messages messages.properties.

La requête activerLogin.action?request_locale=en change la langue de l’application à l’anglais et Struts utilise alors le fichier de messages messages_en.properties.

Créez les fichiers messages.properties et messages_en.properties dans le répertoire /src par exemple. Voici un extrait du fichier messages.properties qui gère la langue française :

index.connexion=Se connecter index.inscription=S’’inscrire index.titre=Page principale : Vente en ligne - EJB 3 / Struts 2 index.langFR=En français

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 206: Ejb Jsf Struts Flex Jasper

index.langEN=En anglais index.phrase1=Choix de la langue header.titre=Vente en ligne footer.titre=Magasin virtuel - Struts 2 / EJB 3 menu.titre=Menu menu.accueil=Accueil menu.profil=Mon profil menu.articles=Liste des articles menu.historique=Historique menu.recherche=Rechercher un article menu.contact=Contactez-nous menu.deconnexion=Deconnexion general.nom=Nom general.prenom=Prénom login.titre=Ecran de login login.phrase=Rentrez vos login et mot de passe login.login=Login login.pwd=Mot de passe login.bouton=Connexion login.retour=Retour à la page principale accueil.bienvenue=Bienvenue 0 1 !

Et voici un extrait du fichier message_en.properties pour la gestion de la langue anglaise :

index.connexion=Log in index.inscription=Register index.titre=Main page : Online shopping - EJB 3 / Struts 2 index.langFR=In french index.langEN=In english index.phrase1=Language choice header.titre=Online shopping footer.titre=Virtual shop - Struts 2 / EJB 3 menu.titre=Menu menu.accueil=Home menu.profil=My profile menu.articles=List of items menu.historique=Previous orders menu.recherche=Search an item menu.contact=Contact us menu.deconnexion=Log out general.nom=Last name general.prenom=First name login.titre=Login screen login.phrase=Enter your login and password login.login=Login login.pwd=Password login.bouton=Log in login.retour=Back to the main page accueil.bienvenue=Welcome 0 1 !

Dans les pages JSPs, les messages sont récupérés via le tag <s:text /> ou bien l’attribut key présent dans certains tags Struts. Une autre méthode consiste à utiliser la méthode OGNL getText(). Par exemple :

<s:property value="getText(’accueil.lienProfil’)"/>

Enfin des paramètres peuvent être ajoutés aux messages, comme c’est le cas du message de bienvenue, qui reprend le nom et le prénom de l’utilisateur connecté :

<h1> <s:text name="accueil.bienvenue" > <s:param ><s:property value="#session.leclient.nom" /> </s:param> <s:param ><s:property value="#session.leclient.prenom" /> </s:param>

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 207: Ejb Jsf Struts Flex Jasper

</s:text> </h1>

Pour faciliter l’édition des messages dans plusieurs langues à la fois et donc dans plusieurs fichiers de façon simultanée, vous pouvez utiliser l’éditeur freeware Babel Fish qui peut être téléchargé à l’URL suivante :

http://www.softpedia.com/get/Others/Home­Education/Babel­Fish.shtml ou bien encore Eclipse ResourceBundle Editor, un plugin pour Eclipse disponible à cette adresse : http://sourceforge.net/projects/eclipse­rbe/.

OGNL et Value Stack

Struts utilise OGNL (Object­Graph Navigation Language), le langage d’expression hérité de WebWork. Il suffit de fournir une méthode getter publique et d’utiliser le tag <s:property /> dans la JSP. Ce tag fournit un accès aux objets (les values) dans la ValueStack OGNL. En effet lors de l’invocation d’une action, un objet ValueStack est créé avant l’exécution de la méthode execute() de l’action. Dans cet objet sont stockés l’action et d’autres objets. L’objet ValueStack agit comme une pile. Il est possible d’y ajouter des objets, de les supprimer ou de les appeler.

Pour debugger, ajoutez le tag <s :debug> à une page JSP et vous verrez un lien [Debug]. Cliquez dessus pour faire apparaître les informations de debug sur le contenu présent dans la Value Stack (objets de la pile)

et la Stack Context.

L’affichage du contenu de la Value Stack grâce au tag <s:debug/> dans la page d’accueil montre que l’objet Client est présent dans cette pile d’objets. Il est en effet utilisé dans l’Action LoginAction lors du contrôle du login et du password :

Vous pouvez activer l’interceptor DebuggingInterceptor en affectant la valeur true au paramètre struts.devMode dans le fichier struts.properties. Cet interceptor donne accès à différents écrans de débuggage pour visualiser les données d’une page en ajoutant le paramètre debug dans l’URL. Voici des exemples d’URLS de ces écrans, avec les valeurs possibles du paramètre debug :

http://localhost:8085/VenteEnLigneWebStruts2/doLogin.action?debug=console : cette URL affiche une console OGNL dans une popup pour tester des expressions OGNL.

http://localhost:8085/VenteEnLigneWebStruts2/doLogin.action?debug=xml : cette URL affiche les paramètres de contexte, de session et la pile de valeurs dans un flux XML.

- 19 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 208: Ejb Jsf Struts Flex Jasper

http://localhost:8085/VenteEnLigneWebStruts2/doLogin.action?debug=browser : cette URL permet de naviguer parmi les données.

5. Contrôle des accès avec les interceptors

À l’instar des EJB 3, Struts 2 introduit les interceptors qui sont similaires à des filtres de servlets et sont utilisés pour lancer des traitements avant et après l’exécution d’une action. Ils peuvent intercepter des paramètres et aider à la validation ou encore au workflow. Les interceptors sont dans ce cas aussi une forme d’AOP (Aspect­Oriented Programming).

Un exemple d’utilisation des interceptors est le contrôle d’accès des pages. Il est souhaitable d’interdire l’accès à des pages si l’utilisateur n’est pas connecté. Par exemple dans le cas d’un copier­coller d’une URL sauvegardée. Un interceptor Struts 2 peut être mis en œuvre dans ce but. L’interceptor suivant est appliqué à la page d’accueil uniquement.

Un interceptor implémente l’interface com.opensymphony.xwork2.interceptor. La classe com.opensymphony.xwork2.interceptor.AbstractInterceptor implémente cette interface et fournit des implémentations par défaut des méthodes init() et destroy(). Elle permet de n’avoir à implémenter que la méthode intercept (ActionInvocation invocation). Cette méthode est appelée avant et après l’invocation de l’action.

L’API du framework XWork est consultable à l’URL suivante : http://www.opensymphony.com/xwork/api/

Dans le package com.eni.dvtejb.clientStruts2.interceptor, créez la classe AutorisationInterceptor.java :

package com.eni.dvtejb.clientStruts2.interceptor; import java.util.Map;

- 20 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 209: Ejb Jsf Struts Flex Jasper

import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ValidationAware; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class AutorisationInterceptor extends AbstractInterceptor private static final long serialVersionUID = 1L; private static final String CLE_UTILISATEUR = "leclient"; public String intercept(ActionInvocation invocation) throws Exception Map session = invocation.getInvocationContext().getSession(); if(session.get(CLE_UTILISATEUR) == null) addActionError(invocation, "Vous devez vous identifier pour accéder à cette page !"); return Action.ERROR; return invocation.invoke(); private void addActionError(ActionInvocation invocation, String message) Object action = invocation.getAction(); if(action instanceof ValidationAware) ((ValidationAware) action).addActionError(message);

Dans login.xml, déclarez l’interceptor et sa classe pour pouvoir le référencer par la suite à l’aide des tags <interceptor> et <interceptor­ref>. Les interceptors sont exécutés dans l’ordre de leur déclaration.

Extrait du fichier login.xml :

<interceptors> <interceptor name="autorisation" class="com.eni.dvtejb.clientStruts2.interceptor.AutorisationIntercep tor" /> <interceptor-stack name="unepile"> <interceptor-ref name="exception" /> <interceptor-ref name="i18n" /> <interceptor-ref name="chain" /> <interceptor-ref name="params" /> <interceptor-ref name="autorisation" /> </interceptor-stack> </interceptors>

L’action accueil est interceptée par l’interceptor autorisation pour vérifier que l’utilisateur est bien connecté. L’interception est déclarée dans le fichier de configuration accueil.xml avec le tag <interceptor­ref> qui permet de référencer un interceptor ou une pile d’interceptors.

Extrait du fichier accueil.xml :

<action name="accueil" > <interceptor-ref name="autorisation"></interceptor-ref> <result type="tiles" name="error" >VenteEnLigne.login</result> <result type="tiles">VenteEnLigne.accueil</result> </action>

Si l’utilisateur est connecté, il est dirigé vers la page d’accueil. Dans le cas contraire, il est renvoyé vers la page d’erreur, dans ce cas la page de login, avec un message d’erreur qui s’affiche, comme illustré dans l’écran ci­dessous.

- 21 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 210: Ejb Jsf Struts Flex Jasper

La pile d’interceptors par défaut, qui sont automatiquement lancés au démarrage de l’application, est incluse dans struts­default.xml. Voici un extrait de cette pile dont le nom est defaultStack :

<!-- Stack par défaut--> <interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> ... </interceptor-stack>

L’interceptor exception peut être utilisé pour intercepter des exceptions et les mapper à des results. Par exemple dans le cas de l’exception javax.ejb.EJBAccessException qui est levée lorsqu’un utilisateur tente d’exécuter une fonctionnalité qui appelle une méthode dont il n’a pas les droits d’exécution.

<interceptors> <interceptor-stack name="appDefault"> <interceptor-ref name="defaultStack"> <param name="exception.logEnabled">true</param> <param name="exception.logLevel">ERROR</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="appDefault" /> <!-- Ce result est disponible pour toutes les actions du package --> <global-results> <result type="tiles" name="PermissionKO">VenteEnLigne.AccesInterdit</result> </global-results> <global-exception-mappings> <exception-mapping result="PermissionKO" exception="javax.ejb.EJBAccessException"></exception-mapping> </global-exception-mappings>

La balise <global­exception­mappings> permet de définir une exception globale et de la mapper à un result global. Ce result est définit dans la balise <global­results>. La page AccesInterdit.jsp, intégrée à Tiles avec le nom VenteEnLigne.AccesInterdit, peut alors être retournée avec un message qui correspond à l’exception générée :

- 22 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 211: Ejb Jsf Struts Flex Jasper

6. Création d’un Service locator

L’injection d’EJB via l’annotation @EJB n’étant pas possible dans une action Struts 2, le design pattern Service locator prend tout son sens. Il est utilisé ici pour faire abstraction des appels JNDI au niveau du serveur d’application, centraliser les accès aux ressources JNDI (et à l’objet InitialContext) tout en améliorant les performances en gérant un cache de ces ressources. Créez la classe ServiceLocator dans le package com.eni.dvtejb.metier.services du projet VenteEnLigneEJB.

La classe singleton ServiceLocator.java permet de gérer une instance unique par machine virtuelle Java :

package com.eni.dvtejb.metier.services; import java.util.HashMap; import javax.naming.InitialContext; import javax.naming.NamingException; public class ServiceLocator // L’instance de cette classe est private pour en faire un singleton private static ServiceLocator serviceLocator = null; InitialContext context = null; // Gestion d’un cache sous la forme d’une HashMap HashMap cache = null; public ServiceLocator() throws NamingException context = new InitialContext(); // Initialisation de la HashMap à 10 objets cache = new HashMap(10); // Retourne l’instance singleton public synchronized static ServiceLocator getInstance() throws NamingException if (serviceLocator == null) serviceLocator = new ServiceLocator(); return serviceLocator; public Object getService(String jndiName) throws NamingException if (!cache.containsKey(jndiName)) // Si l’objet n’est pas dans le cache, faire un lookup et le sauver

- 23 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 212: Ejb Jsf Struts Flex Jasper

cache.put(jndiName, context.lookup(jndiName)); return cache.get(jndiName);

Le design pattern Service Locator, dans le contexte des EJB, est un singleton qui encapsule le « lookup » pour les EJB. Ainsi lors de la création du ServiceLocator, le contexte JNDI est initialisé et une HashMap qui servira de cache est créée. Ce cache permet un gain de performance car si un service (session bean) est déjà présent dans le cache, on ne va pas le chercher à nouveau.

Par la suite, cette classe peut donc être systématiquement utilisée dans les actions Struts 2 pour faire appel à des EJB. Le schéma suivant illustre le design pattern Service locator appliqué aux EJB et à Struts 2 :

7. Affichage de la liste des articles

Pour afficher la liste des articles présents en base, créez une action MagasinAction.java qui contiendra la plupart des méthodes de l’application. La méthode par défaut, execute(), est celle qui prend en charge l’affichage de cette liste d’articles.

Voici un extrait de la classe MagasinAction.java :

package com.eni.dvtejb.clientStruts2.action; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.naming.InitialContext; import org.apache.struts2.interceptor.ParameterAware; import org.apache.struts2.interceptor.SessionAware; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.services.ArticlePanier; import com.eni.dvtejb.metier.services.ServiceLocator; import com.eni.dvtejb.metier.sessions.FacadeRemote; import com.eni.dvtejb.metier.sessions.PanierBeanRemote; import com.opensymphony.xwork2.ActionSupport; public class MagasinAction extends ActionSupport implements

- 24 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 213: Ejb Jsf Struts Flex Jasper

SessionAware, ParameterAware private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(MagasinAction.class); private Map session ; private Map parameters; public Map getParameters() return parameters; public void setParameters(Map parameters) this.parameters = parameters; //@Override public void setSession(Map map) this.session = map; private BigDecimal quantite; public BigDecimal getQuantite() return quantite; public void setQuantite(BigDecimal quantite) this.quantite = quantite; private List catalNom; private List prodNom; private List artNom; private List articleNumero; private List articlePrix; private List stockQtite; // Ecran Commandes private List commandeId; private List articleNom; private List dateCommande; private String numCC; private String typeCC; public String getNumCC() return numCC; public String getTypeCC() return typeCC; public List getStockQtite() return stockQtite; public List getCatalNom() return catalNom; public List getProdNom() return prodNom; public List getArtNom() return artNom;

- 25 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 214: Ejb Jsf Struts Flex Jasper

public List getArticleNumero() return articleNumero; public List getArticlePrix() return articlePrix; // Commandes public List getCommandeId() return commandeId; public List getArticleNom() return articleNom; public List getDateCommande() return dateCommande; public String execute() throws Exception InitialContext initialContext = new InitialContext(); FacadeRemote facade = (FacadeRemote) ServiceLocator.getInstance().getService("VenteEnLigne/FacadeBean/remote"); // Renvoie une liste de Object[] // Le 1er élément est une instance de Catalogue, le 2nd élément est une instance de Produit, // le 3ème élément est une instance d’Article, etc // (CatalogueA, ProduitA, ArticleA), (CatalogueB, ProduitB, ArticleB), (CatalogueC, ProduitC, ArticleC), ... List magasin = facade.findMagasin(); int taille = magasin.size(); // Ces ArrayList sont automatiquement placées dans la stack OGNL catalNom = new ArrayList(); prodNom = new ArrayList(); artNom = new ArrayList(); articleNumero = new ArrayList(); articlePrix = new ArrayList(); stockQtite = new ArrayList(); for (int i = 0; i<taille;i++) Object[] ligne = (Object[])magasin.get(i); // ligne i = (Cataloguei, Produiti, Articlei, ArticleNumeroi, articlePrixi, stockQtitei) String nomCat = (String)ligne[0]; // Cataloguei catalNom.add(nomCat); String nomProd = (String)ligne[1]; // Produiti prodNom.add(nomProd); String nomArt = (String)ligne[2]; // Articlei artNom.add(nomArt); long articleid = (Long)ligne[3]; articleNumero.add(articleid); double prix = (Double)ligne[4]; articlePrix.add(prix); BigDecimal qtite = (BigDecimal)ligne[5]; stockQtite.add(qtite); return SUCCESS; ...

L’interface org.apache.struts2.interceptor.ParameterAware fournit une Map qui contient les paramètres de la requête. Elle oblige à implémenter la méthode void setParameters(Map m). C’est une solution alternative à l’utilisation

- 26 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 215: Ejb Jsf Struts Flex Jasper

d’attributs similaires aux champs des formulaires.

Vous pouvez noter l’utilisation du design pattern Service Locator précédemment défini pour effectuer un lookup du stateful session bean FacadeBean qui sert de Session Façade et qui est en charge de récupérer la liste des articles et de gérer les connexions et profils des utilisateurs.

Le design pattern Façade permet de fournir une façade qui fait le lien entre les interfaces utilisateurs et les objets du domaine.

Les design patterns Service locator et Façade sont décrit dans l’ouvrage de référence émis par Sun « Core J2EE Patterns », également décrit en ligne à l’adresse

http://java.sun.com/blueprints/corej2eepatterns/Patterns/index.html

Les design patterns ont été popularisés par le livre « Design patterns, catalogue de modèles de conception réutilisables » écrit par le Gang of Four (Erich Gamma,Richard Helm, Ralph Johnson, John Vlissides).

Dans le projet VenteEnLigneEJB, ajoutez l’interface FacadeRemote et le stateful session bean FacadeBean.

Le code de l’interface FacadeRemote.java est le suivant :

package com.eni.dvtejb.metier.sessions; import java.util.List; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Utilisateur; public interface FacadeRemote public Utilisateur findUtilisateurByLoginPwd(String login, String pwd); public Utilisateur findUtilisateurById(long utilisateurId); public Adresse findAdresseById(long adresseId); public boolean updateClient(Client client, Adresse adresse); public List findMagasin();

Et voici un extrait du stateful session bean FacadeBean.java :

package com.eni.dvtejb.metier.sessions; import java.io.Serializable; import java.util.List; import javax.ejb.Remote; import javax.ejb.Stateful; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.Query; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Utilisateur; // Stateful pour pouvoir utiliser PersistenceContextType.EXTENDED @Stateful @Remote (FacadeRemote.class) public class FacadeBean implements FacadeRemote, Serializable private static final long serialVersionUID = 1L; @PersistenceContext(unitName = "VenteEnLigneClientJavaEE", type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; public List findMagasin() return entityManager.createQuery("select c.nom, p.nom, a.nom,

- 27 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 216: Ejb Jsf Struts Flex Jasper

a.articleid, a.prix, s.quantite from Catalogue c, Produit p, Article a, Stock s" + " where c.catalogueid = p.catalogueFk" + " and p.produitid = a.produitFk" + " and a.stockFK = s.stockid").getResultList(); ...

L’unité de persistance est VenteEnLigneClientJavaEE. Elle est définie dans le fichier de persistance persistence.xml dans le répertoire META­INF du projet VenteEnLigneClient.

Enfin, au niveau de la couche de présentation, ajoutez la page JSP magasin.jsp dans le répertoire /magasin du projet VenteEnLigneWebStruts2 :

<%@ taglib prefix="s" uri="/struts-tags" %> <link href="<s:url value="/css/styles.css"/>" rel="stylesheet" type="text/css"/> <html> <body> <center> <div align="right" > <img src="<%=request.getContextPath()%>/images/utilisateur.gif" ><br> <s:text name="general.nom" /> : <s:property value="#session.leclient.nom" /><br> <s:text name="general.prenom" /> : <s:property value="#session.leclient.prenom" /> </div> <h1><s:text name="magasin.titre" /></h1> <table class="table1" > <tr> <td> <table> <th bgcolor="#3333FF"><font color="#FFFFFF"><s:text name="magasin.catalogue" /></font></th> <s:iterator value="catalNom" > <tr bgcolor="#478D72"> <td height="35"> <font color="#FFFFFF"><s:property /></font> </td> </tr> </s:iterator> </table> </td> <td> <table> <th bgcolor="#3333FF"><font color="#FFFFFF"><s:text name="magasin.produit" /></font></th> <s:iterator value="prodNom" > <tr bgcolor="#478D72"> <td height="35"> <font color="#FFFFFF"><s:property /></font> </td> </tr> </s:iterator> </table> </td> <td > <table> <th bgcolor="#3333FF"><font color="#FFFFFF"><s:text name="magasin.article" /></font></th> <s:iterator value="artNom" > <tr bgcolor="#478D72"> <td height="35"> <font color="#FFFFFF"><s:property /></font> </td>

- 28 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 217: Ejb Jsf Struts Flex Jasper

</tr> </s:iterator> </table> </td> <td> <table> <th bgcolor="#3333FF"><font color="#FFFFFF"><s:text name="magasin.prix" /></font></th> <s:iterator value="articlePrix" > <tr bgcolor="#478D72"> <td height="35"> <font color="#FFFFFF"><s:property /></font> </td> </tr> </s:iterator> </table> </td> <td> <table> <th bgcolor="#3333FF"><font color="#FFFFFF"><s:text name="magasin.stock" /></font></th> <s:iterator value="stockQtite" > <tr bgcolor="#478D72"> <td height="35"> <font color="#FFFFFF"><s:property /></font> </td> </tr> </s:iterator> </table> </td> <td> <table> <th bgcolor="#3333FF"><font color="#FFFFFF"><s:text name="magasin.details" /></font></th> <s:iterator value="articleNumero" > <tr bgcolor="#B9DBCC"> <td height="35"> <s:url id="visualiser" action="visualiser" > <s:param name="numeroId" value="%top"/> </s:url> <s:a href="%visualiser" ><s:text name="magasin.visualiser" /> <img src="<%=request.getContextPath()%>/images/loupe.png"> </s:a> </td> </tr> </s:iterator> </table> </td> <tr> </table> <br/> <s:url id="afficheC" action="afficherPanier"/><s:a href="%afficheC"><img src="<%= request.getContextPath() %>/images/caddie.gif"> <br><s:text name="magasin.lien" /></s:a> </center> </body> </html>

Cette JSP utilise le tag <s:iterator> pour récupérer les valeurs contenues dans des listes provenant de l’action MagasinAction.

Pour utiliser ces listes dans des JSPs, il suffit de déclarer ces listes dans l’action et de définir un getter pour chacune de ces listes.

Le tag <s:iterator> récupère les valeurs du getter getXXX() correspondant à l’objet dans la value stack, itère sur ces valeurs et les affiche avec le tag <s:property/>.

- 29 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 218: Ejb Jsf Struts Flex Jasper

La valeur TOP du tag <s:property> permet de récupérer la valeur au sommet de la stack (pile) dans l’itération en cours.

L’id de l’article est ainsi transmis pour faire une recherche par article.

Le framework Struts 2 définit deux types de balises (tags) :

Les Generic tags : ils contrôlent l’exécution lors de la construction dynamique de la page JSP.

À l’intérieur de ces Generic tags se trouvent les Control tags pour les contrôles de flow, les Data tags pour la manipulation des données.

Les UI (User Interface) tags : ils sont principalement destinés à utiliser les données des Data tags, des actions et de la value stack.

Ils sont divisés en trois types : les Form tags, les Non­Form tags et les tags Ajax qui permettent de développer des applications avec des interfaces riches (Rich Internet Applications).

Voici un tableau qui liste les tags dans Struts 2 :

La configuration dans le fichier magasin.xml pour cette action est la suivante :

<package name="magasin" extends="login"> <!-- DEBUT MAGASIN --> <action name="afficheMagasin" class="com.eni.dvtejb.clientStruts2.action.MagasinAction"> <result type="tiles" name="success">VenteEnLigne.afficheMagasin</result> </action> ... <!-- FIN MAGASIN -->

Generic tags UI Tags

Control tags Data tags Form tags Non­Form tags Tags Ajax

if

elseif

else

append

generator

iterator

merge

sort

subset

a

action

bean

date

debug

i18n

include

param

property

push

set

text

url

checkbox

checkboxlist

combobox

doubleselect

head

file

form

hidden

label

optgroup

password

radio

reset

select

submit

textarea

textfield

token

updownselect

actionerror

actionmessage

component

div

fielderror

a

autocompleter

bind

datetimepicker

div

head

submit

tabbedpanel

textarea

tree

treenode

- 30 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 219: Ejb Jsf Struts Flex Jasper

</package>

Dans le fichier de configuration tiles.xml la page centrale a été redéfinie pour pointer sur la JSP magasin.jsp.

<definition name="VenteEnLigne.afficheMagasin" extends="TemplateStandard"> <put-attribute name="title" value="Liste des articles" /> <put-attribute name="body" value="/magasin/magasin.jsp" /> </definition>

La page affiche la liste des articles sous forme de tableau. L’utilisateur a la possibilité de cliquer sur le lien Visualiser pour afficher les détails des articles.

Traitement d’une requête par Struts 2 :

Lorsqu’une requête HTTP est émise, elle est interceptée par le filtre StrutsPrepareAndExecuteFilter défini dans le fichier web.xml et qui renvoie vers l’Action appropriée à partir de la configuration définie dans le fichier struts.xml.

Le filtre StrutsPrepareAndExecuteFilter détermine l’Action à invoquer à partir de la configuration du fichier struts.xml et l’invoque après avoir appelé la pile d’intercepteurs qui est associée à cette Action. Les intercepteurs sont appelés dans l’ordre de configuration de la pile.

Une fois terminé l’appel de la pile des intercepteurs, c’est au tour de la méthode execute() de l’Action d’être appelée. Celle­ci renvoie un ’result’ qui est associé à une page JSP.

La page JSP est exécutée et génère la réponse. Cette réponse est interceptée par la pile d’intercepteurs, dans l’ordre inverse de leur configuration dans la pile.

Enfin le filtre StrutsPrepareAndExecuteFilter repasse le contrôle au web container pour qu’il envoie la réponse HTTP au navigateur.

Ce filtre implémente le design pattern Front Controller.

8. Visualisation des détails d’un article

La visualisation des détails d’un article est pris en charge par les actions MagasinAction et AffichageImageAction.

- 31 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 220: Ejb Jsf Struts Flex Jasper

Les images des articles sont stockées en base dans la colonne IMAGE de la table ARTICLE. Le mapping est fait en utilisant un champ image qui est un tableau de bytes stocké en tant que Blob en base grâce à l’annotation @javax.persistence.Lob dans l’entity bean Article :

package com.eni.dvtejb.metier.entities; import java.io.Serializable; import java.util.Set; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.Lob; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @Entity public class Article implements Serializable ... @Lob @Basic(fetch=FetchType.LAZY) private byte[] image; public byte[] getImage()

- 32 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 221: Ejb Jsf Struts Flex Jasper

return image; public void setImage(byte[] image) this.image = image; ...

De manière générale, les étapes pour la récupération d’une image en base et son affichage se résument à :

1 ­ Faire une requête en base qui retourne un article et son image au format Blob.

2 ­ Appeler la méthode getBinaryStream() de la classe java.sql.Blob. Cette méthode retourne un type InputStream qui permet de facilement utiliser l’image.

3 ­ Définir le type de contenu de la réponse HttpServletResponse comme étant une image au format jpg avec la chaîne « image/jpg ». Ceci est possible grâce à la servlet org.apache.struts2.ServletActionContext qui donne accès aux informations de contexte des actions (paramètres de servlet, attributs de requêtes, session Http etc).

4 ­ Écrire l’image sur OutputStream, le flux de sortie de la servlet HttpServletResponse.

L’action AffichageImageAction fait usage de l’interface org.apache.struts2.ServletActionContext pour récupérer l’id de l’article qui est dans la requête :

package com.eni.dvtejb.clientStruts2.action; import java.io.InputStream; import java.io.OutputStream; import java.sql.Blob; import javax.servlet.http.HttpServletResponse; import javax.sql.rowset.serial.SerialBlob; import org.apache.struts2.ServletActionContext; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.services.ServiceLocator; import com.eni.dvtejb.metier.sessions.PanierBeanRemote; import com.opensymphony.xwork2.ActionSupport; public class AffichageImageAction extends ActionSupport private static final long serialVersionUID = 1L; private static final Logger log =

- 33 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 222: Ejb Jsf Struts Flex Jasper

Logger.getLogger(AffichageImageAction.class); public String execute() throws Exception PanierBeanRemote panierBeanRemote = (PanierBeanRemote) ServiceLocator.getInstance().getService("VenteEnLigne/PanierBean/remote"); String numeroId = (String)ServletActionContext.getRequest().getParameter("numeroId"); long l = Long.parseLong(numeroId); Article monArticle = panierBeanRemote.findById(l); HttpServletResponse response = ServletActionContext.getResponse(); OutputStream out = response.getOutputStream(); // Un Blob est essentiellement un tableau de bytes Blob image = null; image = new SerialBlob(monArticle.getImage()); InputStream input = image.getBinaryStream(); // Afficher l’image Blob sur la réponse HttpServletResponse response.setContentType("image/jpg"); int taille = 0; int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; while ((taille = input.read(buffer)) != -1) out.write(buffer, 0, taille); return null;

9. Recherche d’articles

Pour aider le client à trouver les articles qui l’intéresse, le site doit fournir une fonctionnalité de recherche. La recherche peut par exemple se faire sur le nom de l’article et une fourchette de prix [prix minimum ­ prix maximum]. La liste des articles renvoyée affichera les articles qui satisfont les critères de recherche renseignés. Le schéma suivant illustre le fonctionnement de la recherche :

La page JSP de recherche utilise le tag Ajax Dojo <sx:autocompleter> qui permet de faire de la suggestion de noms d’articles au fur et à mesure que l’utilisateur rentre un nom d’article. Pour pouvoir utiliser ce tag, vous devez obligatoirement inclure le tag <sx:head> dans la JSP :

- 34 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 223: Ejb Jsf Struts Flex Jasper

<sx:head debug="true" /> ... <sx:autocompleter label="Nom" list="nomsArticles" name="nomArticle" tooltip="Rentrez le nom de l’article recherché" autoComplete="true" value="Tous"/> <s:textfield name="prixMin" label="Prix minimum" value="0" tooltip="Rentrez le prix minimum souhaité"/> <s:textfield name="prixMax" label="Prix maximum" value="10000" tooltip="Rentrez le prix maximum souhaité"/>

Le tag <sx:autocompleter> contient de nombreux attributs dont l’attribut list qui permet de spécifier l’origine des données qui vont alimenter ce tag. L’alimentation des données est fait en amont par l’appel, dès que le client arrive sur la page de recherche, de la méthode rechercheNomsArticles() dans l’Action RechercheAction. L’écran suivant montre l’autocomplétion en action dans la JSP recherche.jsp. Lorsque le client commence à taper une lettre, la dropdown list affiche des noms d’articles qui commencent par cette lettre :

La liste des articles est préalablement chargée, de façon asynchrone, dans une collection de type List dans l’Action RechercheAction.java dont voici un extrait :

public class RechercheAction extends ActionSupport ... //Pour AJAX autocomplete private List nomsArticles; public List getNomsArticles() return nomsArticles;

- 35 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 224: Ejb Jsf Struts Flex Jasper

public String rechercheGlobal() throws Exception ... // Lors du premier affichage de la page, les champs sont vides if ((getNomArticle().equals("")) && (null == getPrixMin()) && (null == getPrixMax())) nomsArticles = rechercheNomsArticles(); log.info("cas INPUT"); return INPUT; else RechercheRemote rechercheRemote = (RechercheRemote) ServiceLocator.getInstance().getService("VenteEnLigne/RechercheBean/ remote"); List _articles = rechercheRemote.rechercheArticles(critNomArticle, critPrixMin, critPrixMax); int taille = _articles.size(); Article temp = new Article(); for (int i = 0; i<taille;i++) Object[] ligne = (Object[])_articles.get(i); BigDecimal articleId = (BigDecimal)ligne[0]; String nomArt = (String)ligne[1]; BigDecimal prixArt = (BigDecimal)ligne[2]; temp = new Article(articleId.longValue(), nomArt, prixArt.doubleValue()); articles.add(i, temp); int total = 0; if (null != articles) total = articles.size(); return SUCCESS; // Fin else public List rechercheNomsArticles() throws Exception RechercheRemote rechercheRemote = (RechercheRemote) ServiceLocator.getInstance().getService("VenteEnLigne/RechercheBean/ remote"); List _articles = rechercheRemote.rechercheNomArticles(); return _articles;

Un stateless session bean (RechercheBean.java) dédié à la recherche est mis en place :

@Stateless @Remote (RechercheRemote.class) public class RechercheBean implements RechercheRemote, Serializable private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(RechercheBean.class); @PersistenceContext(unitName = "VenteEnLigneModuleEJB", type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; public List<Article> rechercheArticles(String nomArticle, BigDecimal prixMinimum, BigDecimal prixMaximum) Query query = null; if (nomArticle.equals("Tous")) query = entityManager.createNativeQuery("SELECT a.articleid, a.nom, a.prix

- 36 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 225: Ejb Jsf Struts Flex Jasper

FROM article a WHERE " + " a.prix between ?1 and ?2"); query.setParameter(1, prixMinimum); query.setParameter(2, prixMaximum); else query = entityManager.createNativeQuery("SELECT a.articleid, a.nom, a.prix FROM article a WHERE " + " a.nom = ?1 AND a.prix between ?2 and ?3"); query.setParameter(1, nomArticle); query.setParameter(2, prixMinimum); query.setParameter(3, prixMaximum); List articles = query.getResultList(); return articles; // Méthode pour autocomplete AJAX Dojo public List rechercheNomArticles() Query query = entityManager.createNativeQuery("SELECT a.nom FROM article a"); List nomArticles = query.getResultList(); return nomArticles;

10. Passage de la commande

Lorsque le client a fini sa sélection d’articles, il doit passer à l’étape du règlement de la commande. Ce règlement consiste pour le client à entrer les détails de sa carte de crédit. La vérification du numéro de carte de crédit s’effectue via un stateless session bean exposé en tant que Web Service, comme vu dans le chapitre Utilisation des Web Services.

L’essentiel du traitement est fait dans la méthode payer() de l’Action PaiementAction dont voici un extrait :

public class PaiementAction extends ActionSupport implements SessionAware, ParameterAware ... public String payer() throws Exception boolean isValide = false ; // Appel du stateless SB exposé en tant que Web service VerifierCarteCredit verifiercc = (VerifierCarteCredit) ServiceLocator.getInstance().getService("VenteEnLigne/VerifierCarteC reditBean/remote"); isValide = verifiercc.validerCarteCredit(getTypeCC(), getNumCC()); if (isValide) Client cl = (Client)session.get("leclient"); PanierBeanRemote panierBeanRemote = (PanierBeanRemote) ServiceLocator.getInstance().getService("VenteEnLigne/PanierBean/remote"); ArrayList<ArticlePanier> articlesPanier; articlesPanier = panierBeanRemote.getPanier(); java.util.Date utilDate = stringToDate(getExpirationDate()); java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); panierBeanRemote.commander(cl, articlesPanier, numCC, typeCC, sqlDate); return SUCCESS; else addActionError("Le numero de carte de credit " + numCC + " est invalide"); log.info("Le numero de carte de credit " + numCC + " est invalide"); return ERROR;

- 37 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 226: Ejb Jsf Struts Flex Jasper

Si le numéro de carte de crédit est valide, le paiement de la commande peut continuer et les détails de la commande (date expiration, date commande, n° carte crédit, type, adresse, utilisateur) sont insérées en base dans la table COMMANDE.

Les données sensibles telles que les numéros de carte de crédit doivent être cryptées. La CNIL précise d’ailleurs que ces numéros ne devraient être conservés que le temps de la réalisation des transactions. Dans

le cas contraire, l’accord du client est nécessaire. Des algorithmes d’encryptage/décryptage existent et peuvent être appliqués à différents stades de l’application. Par exemple dans le stateless session bean qui insère les données de la commande ou bien de façon transparente dans la base de données Oracle, au niveau de la déclaration du type de la colonne NUMERO_CARTECREDIT lors de la création de la table.

La page payer.jsp récapitule le montant total de la commande et invite donc le client à entrer les détails de sa carte de crédit pour finaliser sa commande.

<s:form action="payer" method="post" theme="xhtml" tooltipConfig="#’tooltipIcon’:’/images/info.png’" > <s:actionerror /> <table class="table1" > <tr> <s:select key="paiement.typecc" name="typeCC" headerKey="-1" headerValue="Selectionnez un type" list="#’VISA’:’Visa’, ’MASTERCARD’:’Eurocard / MasterCard’, ’AMEX’:’American Express’,’CB’:’Carte Bancaire’, ’CA’:’Carte Aurore’" value="typeccSelectionne" required="true" /> </tr> <tr> <s:textfield name="numCC" key="paiement.numcc" maxLength="50" size="20" required="true" /> </tr> <tr> <s:textfield name="nomTitulaire" key="paiement.nomcc" maxLength="50" size="20" required="true"/> </tr> <tr> <s:textfield name="codeSecurite" key="paiement.codesecu" tooltip="%getText(’paiement.tooltip’)" required="true"/> </tr> <tr> <td><s:text name="paiement.dateexpi" />:</td> <td> <sx:datetimepicker name="expirationDate" key="paiement.dateexpiMois" displayFormat="MM/yyyy" required="true"/> </td> </tr> <s:submit key="paiement.bouton" align="center"/> <s:reset value="Reset" align="center"/> </table> </s:form>

Les types possibles de la carte de crédit sont listés directement à l’intérieur du tag <s:select>. Le tag Ajax Dojo <sx:datetimepicker> permet d’afficher un calendrier pour choisir la date. De nombreux formats de date sont supportés par l’intermédiaire de l’attribut displayFormat.

- 38 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 227: Ejb Jsf Struts Flex Jasper

La commande est insérée en base dans la table COMMANDE pour pouvoir être historisée et un mail de confirmation de commande est envoyé au client.

@Interceptors(CommanderInterceptor.class) public void commander(Client client, ArrayList<ArticlePanier> articlesPanier, String numCC, String typeCC, java.sql.Date expirationDate) Client cl = em.find(Client.class, client.getUtilisateurid()); Adresse adresse = em.find(Adresse.class, 1L); Commande commande = new Commande(); ArrayList<Lignecommande> lignesCommande = new ArrayList<Lignecommande>(); ArticlePanier artPanier = new ArticlePanier(); for (Iterator it = articlesPanier.iterator (); it.hasNext (); ) artPanier = (ArticlePanier)it.next (); Lignecommande lc = new Lignecommande(); lc.setArticleFk(artPanier.getArticle()); lc.setQuantite(artPanier.getQuantite()); em.persist(lc); lignesCommande.add(lc); Set lignesCommandeSet = new HashSet(lignesCommande); commande.setAdresseFk(adresse); commande.setUtilisateurFk(cl); Date aujourdhui = new Date(); long t = aujourdhui.getTime(); java.sql.Date aujourdhuiSQL = new java.sql.Date(t); commande.setDatecommande(aujourdhuiSQL); commande.setDateExpirationCartecredit(expirationDate); commande.setNumeroCartecredit(numCC); commande.setTypeCartecredit(typeCC); commande.setLignecommandeCollection(lignesCommandeSet); em.persist(commande); // Envoi de mail de façon asynchrone producteurMail(commande);

- 39 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 228: Ejb Jsf Struts Flex Jasper

11. Validation de l’inscription d’un nouvel utilisateur

L’inscription d’un nouvel utilisateur peut se faire par la création d’un wizard sur 3 pages de formulaires.

Avec Struts 2 on peut transférer de façon automatique les données d’un formulaire vers un objet du domaine grâce à l’interceptor params, qui est inclus dans la liste des interceptors du fichier de configuration par défaut, struts­default.xml, dont il vaut mieux hériter.

La cinématique des étapes de création d’un nouvel utilisateur est la suivante :

Créez un nouveau répertoire Webcontent/inscription pour contenir les JSP spécifiques à la création d’un nouvel utilisateur. Une première page permet à l’utilisateur de choisir un login et un password. Une deuxième page demande à l’utilisateur d’entrer son nom, prénom, etc. Enfin une troisième page permet d’entrer l’adresse. À chaque étape, l’utilisateur a la possibilité de modifier les informations entrées précédemment.

L’étape 1 est affichée avec la JSP nouvLogin.jsp :

<%@ taglib prefix="s" uri="/struts-tags" %> <link href="<s:url value="/css/styles.css"/>" rel="stylesheet" type="text/css"/> <s:head theme="xhtml" /> <s:debug/> <center> <h1>Etape 1: Identifiants de connexion</h1> <s:form action="nouvLogin" method="POST" theme="xhtml" > <tr> <td colspan="2"> Choisissez un login et un password</td> </tr> <tr> <td colspan="2"> <s:actionerror /> </td> </tr> <s:textfield name="client.login" key="nouvLogin.login" required="true"/> <s:password name="client.password" key="nouvLogin.pwd" required="true"/> <s:submit value="Valider" align="center"/> </s:form> <br/> </center>

L’enchaînement des écrans de création d’un nouvel utilisateur est le suivant :

- 40 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 229: Ejb Jsf Struts Flex Jasper

- 41 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 230: Ejb Jsf Struts Flex Jasper

Une action unique, UtilisateurAction.java, gère l’enchaînement des écrans, par l’intermédiaire d’un mapping vers la méthode adéquate dans le fichier struts.xml.

Action UtilisateurAction.java :

package com.eni.dvtejb.clientStruts2.action; import javax.naming.InitialContext; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.sessions.ClientDAO; import com.opensymphony.xwork2.ActionSupport;

- 42 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 231: Ejb Jsf Struts Flex Jasper

public class UtilisateurAction extends ActionSupport private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(UtilisateurAction.class); private String email; public String getEmail() return email; public void setEmail(String email) this.email = email; private Client client; private Adresse adresse; public Adresse getAdresse() return adresse; public void setAdresse(Adresse adresse) this.adresse = adresse; public UtilisateurAction() public String execute() return SUCCESS; public Client getClient() return client; public void setClient(Client client) this.client = client; public String validateLogin() throws Exception boolean valide = true; if (client.getLogin().length()== 0) addFieldError("client.login", "Login obligatoire"); valide = false; if (client.getPassword().length()== 0) addFieldError("client.password", "Password obligatoire"); valide = false; if (valide) return SUCCESS; else return INPUT; public String validateUtilisateur() throws Exception boolean valide = true; if (client.getLogin().length()== 0) addFieldError("client.login", "Login obligatoire"); valide = false;

- 43 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 232: Ejb Jsf Struts Flex Jasper

if (client.getPassword().length()== 0) addFieldError("client.password", "Password obligatoire"); valide = false; if (client.getNom().length()== 0) addFieldError("client.nom", "Nom obligatoire"); valide = false; if (client.getPrenom().length()== 0) addFieldError("client.prenom", "Prenom obligatoire"); valide = false; if (client.getTelephone() == null) addFieldError("client.telephone", "Telephone obligatoire"); valide = false; if (client.getTitre().length()== 0) addFieldError("client.titre", "Titre obligatoire"); valide = false; if (client.getFax() == null) addFieldError("client.fax", "Fax obligatoire"); valide = false; if (client.getEmail().length()== 0) addFieldError("client.email", "Email obligatoire"); valide = false; if (valide) return SUCCESS; else return INPUT; public String creerNouvelUtilisateur() throws Exception log.info("Création d’un nouvel utilisateur"); boolean valide = true; if (client.getLogin().length()== 0) addFieldError("client.login", "Login obligatoire"); valide = false; if (client.getPassword().length()== 0) addFieldError("client.password", "Password obligatoire"); valide = false; if (client.getNom().length()== 0) addFieldError("client.nom", "Nom obligatoire"); valide = false; if (client.getPrenom().length()== 0) addFieldError("client.prenom", "Prenom obligatoire"); valide = false; if (client.getTelephone() == null) addFieldError("client.telephone", "Telephone obligatoire");

- 44 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 233: Ejb Jsf Struts Flex Jasper

valide = false; if (client.getTitre().length()== 0) addFieldError("client.titre", "Titre obligatoire"); valide = false; if (client.getFax() == null) addFieldError("client.fax", "Fax obligatoire"); valide = false; if (client.getEmail().length()== 0) addFieldError("client.email", "Email obligatoire"); valide = false; if (adresse.getCodepostal() == null) addFieldError("adresse.codepostal", "Code postal obligatoire"); valide = false; if (adresse.getDepartement().length()== 0) addFieldError("adresse.departement", "Departement obligatoire"); valide = false; if (adresse.getNumero() == null) addFieldError("adresse.numero", "Numero obligatoire"); valide = false; if (adresse.getPays().length()== 0) addFieldError("adresse.pays", "Pays obligatoire"); valide = false; if (adresse.getRue().length()== 0) addFieldError("adresse.rue", "Rue obligatoire"); valide = false; if (adresse.getVille().length()== 0) addFieldError("adresse.ville", "Ville obligatoire"); valide = false; if (!valide) return INPUT; InitialContext initialContext = new InitialContext(); ClientDAO clientDAO = (ClientDAO) ServiceLocator.getInstance().getService("VenteEnLigne/ClientDAOImpl/ remote"); client.setAdresseFk(adresse); try clientDAO.save(client); catch (Exception e ) addFieldError("client.email", "Cette adresse email est déjà utilisée. Veuillez rentrez une adresse email différente."); return INPUT; return SUCCESS;

La sauvegarde en base des données de l’utilisateur et de son adresse se fait avec le stateless session bean

- 45 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 234: Ejb Jsf Struts Flex Jasper

ClientDAO, qui a été développé dans le chapitre Les session beans.

La méthode creerNouvelUtilisateur() est appelée lors du SUBMIT du dernier formulaire, c’est­à­dire celui qui contient toutes les données.

Le fichier de configuration struts.xml peut être modularisé en utilisant la balise d’inclusion <include>.

Cette modularisation a plusieurs avantages comme la possibilité de ne déclarer qu’une seule fois une valeur par défaut dans un fichier et de faire hériter ce fichier par tous les autres.

Un autre avantage est la possibilité de mieux séparer les composants de configuration d’une application web, permettant par exemple le développement de chaque composant par une équipe en particulier.

L’ordre est important : un fichier XML dépendant d’un autre fichier XML doit être obligatoirement placé après celui­ci. Les fichiers sont donc chargés dans l’ordre de leur déclaration dans struts.xml.

Voici le contenu du fichier struts.xml après modularisation :

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <include file="login.xml" /> <include file="accueil.xml" /> <include file="magasin.xml" /> <include file="inscription.xml" /> </struts>

Le premier fichier, login.xml, hérite de struts­default.xml, qui se trouve dans l’archive struts2­core­2.x.jar. La configuration du fichier inscription.xml pour la partie inscription est donc au final la suivante :

<package name="inscription" extends="login"> <action name="inscription"> <result>/inscription/nouvLogin.jsp</result> </action> <action name="nouvLogin" class="com.eni.dvtejb.clientStruts2.action.UtilisateurAction" method="validateLogin"> <result name="input">/inscription/nouvLogin.jsp</result> <result name="error">/inscription/nouvLogin.jsp</result> <result name="success">/inscription/nouvUtilisateur.jsp</result> </action> <action name="confirmDetails" class="com.eni.dvtejb.clientStruts2.action.UtilisateurAction" method="validateUtilisateur"> <result name="input">/inscription/nouvUtilisateur.jsp</result> <result name="success">/inscription/nouvAdresse.jsp</result> </action> <action name="confirmAdresse" class="com.eni.dvtejb.clientStruts2.action.UtilisateurAction" method="creerNouvelUtilisateur"> <result name="input">/inscription/nouvAdresse.jsp</result> <result name="success">/inscription/succes.jsp</result> </action> </package>

La validation des données peut s’effectuer côté serveur ou côté client.

Configuration du fichier struts.xml

Validation côté serveur

- 46 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 235: Ejb Jsf Struts Flex Jasper

La validation de l’inscription a été faite côté serveur, directement dans les méthodes de l’action UtilisateurAction. Struts 2 permet également de gérer la validation d’un formulaire par l’intermédiaire d’un fichier xml dont le nom respecte la norme <nomClasseAction>­validation.xml et qui doit être créé dans le même package que la classe Action.

Par exemple, le fichier Utilisateur­action.xml suivant, situé dans le package com.eni.dvtejb.clientStruts2.action, valide le bon format de l’adresse email :

<validators> <field name="client.email"> <field-validator type="email"> <message >Merci de rentrer une adresse email valide !</message> </field-validator> </field> </validators>

La validation peut être automatiquement gérée par JavaScript. Par exemple, dans nouvLogin.jsp, il suffit d’ajouter l’attribut validate="true" au tag <s:form> de la page JSP.

<s:form action="nouvLogin" method="POST" theme="xhtml" validate="true" > ... </s:form>

Un fichier validation.js est alors inclus dans la page générée dont voici une partie du code :

<script type="text/javascript" src="/VenteEnLigneWebStruts2/struts/xhtml/validation.js"></script> <script type="text/javascript" src="/VenteEnLigneWebStruts2/struts/utils.js"></script> <form id="nouvLogin" name="nouvLogin" onsubmit="return validateForm_nouvLogin();" action="/VenteEnLigneWebStruts2/nouvLogin.action" method="POST" onreset="clearErrorMessages(this);clearErrorLabels(this);" > ... </form> <script type="text/javascript"> function validateForm_nouvLogin() form = document.getElementById("nouvLogin"); clearErrorMessages(form); clearErrorLabels(form); var errors = false; var continueValidation = true; return !errors; </script>

L’injection d’EJB avec l’annotation @EJB dans des actions Struts 2 n’est pas possible. La raison étant que ce n’est pas le container JBoss qui instancie les actions mais le framework Struts lui­même. Seuls les objets

managés (managed beans, servlets, EJB…) peuvent profiter de l’injection fournie par le container. Il existe cependant des plugins tels que « EJB 3 plugin », « struts2ejb3­jboss­plugin » ou des frameworks légers d’injection de dépendance comme Guice, qui permettent l’injection de session beans dans des actions Struts 2.

12. Interface d’administration avec Ajax et Dojo

Dans cette interface, l’utilisateur (Gestionnaire ou Administrateur) a la possibilité, suivant ses droits, de procéder à des opérations de type CRUD (Création, Lecture, Mise à jour, Suppression) sur les clients et les articles.

La nouvelle génération d’applications Web, appelée Web 2.0, est plus proche des applications de bureau. La technologie Ajax (Asynchronous JavaScript and XML) y contribue grandement en facilitant le développement d’interfaces Web plus riches et plus interactives. L’objet XMLHttpRequest est l’objet qui permet à JavaScript de faire des appels de

Validation côté client

- 47 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 236: Ejb Jsf Struts Flex Jasper

requêtes HTTP de façon asynchrone pour pouvoir mettre à jour les données d’une page avec la réponse reçue et ceci sans rafraîchir la page. Le schéma ci­dessous illustre ce fonctionnement :

Struts 2 facilite le développement d’applications interactives avec l’intégration de frameworks Open Source Ajax/JavaScript tels que jQuery ou Dojo sous forme de plugins. Ainsi avec Dojo il est possible de déclencher des évènements qui vont appeler des actions. Ces évènements sont en fait des topics. Le schéma suivant illustre l’interaction entre les actions Struts et les pages JSP qui font usage du plugin Dojo Toolkit, et en particulier des topics :

La taglib suivante référence la librairie de tags DOJO dans une page JSP :

<%@ taglib prefix="sx" uri="/struts-dojo-tags" %>

Le tag Dojo <sx:div> génère une balise HTML <div> et charge son contenu de façon asynchrone. Il permet également de rafraîchir son contenu à intervalles réguliers. Les topics peuvent recharger son contenu avec l’utilisation de l’attribut listenTopics :

<s:url action="list2" id="descrsUrl"/> <div style="width: 1400px;border-style: solid"> <div style="text-align: right;"> <sx:a notifyTopics="/rafraichir" >Rafraîchir la liste <img src="<%=request.getContextPath()%>/images/reload_page.png" ></sx:a> </div> <sx:div id="utilisateurs" href="%descrsUrl" loadingText="Chargement en cours ..." listenTopics="/rafraichir" showLoadingText="true" /> </div>

Dans la page adminUtilisateurs.jsp, lorsque l’utilisateur clique sur le lien « Rafraichir la liste » , le tag <sx:a> publie le

- 48 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 237: Ejb Jsf Struts Flex Jasper

topic /rafraichir avec l’attribut notifyTopics. Le tag <sx:div> spécifie qu’il est à l’écoute du topic /rafraichir avec l’attribut listenTopics et appelle l’action list2 dès que le topic est publié. L’action list2 est chargée de retourner le nouveau contenu de la table des utilisateurs. La liste des utilisateurs (listeClients.jsp) est chargée une première fois quand la page s’affiche pour la première fois et est rechargée à chaque fois qu’un topic est publié.

Deux autres topics sont utilisés dans la JSP adminUtilisateurs.jsp d’administration des clients :

Le topic /sauver pour mettre à vide les champs des formulaires, en bas de page, de modification et d’ajout de client lorsque la modification ou la création est faite.

Le topic /modif pour récupérer les données du client à modifier et les afficher dans le formulaire de modification en bas de page.

Extrait de la JSP adminUtilisateurs.jsp :

<sx:head parseContent="true" debug="false"/> <script type="text/javascript"> dojo.event.topic.subscribe("/sauver", function(data, type, request) if(type == "load") // Champs du formulaire de modification dojo.byId("utilisateurid").value = ""; dojo.byId("prenom").value = ""; dojo.byId("nom").value = ""; dojo.byId("titre").value = ""; dojo.byId("email").value = ""; dojo.byId("login").value = ""; dojo.byId("password").value = ""; dojo.byId("fax").value = ""; dojo.byId("telephone").value = ""; // Champs du formulaire d’ajout dojo.byId("prenomAjout").value = ""; dojo.byId("nomAjout").value = ""; dojo.byId("titreAjout").value = ""; dojo.byId("emailAjout").value = ""; dojo.byId("loginAjout").value = ""; dojo.byId("passwordAjout").value = ""; dojo.byId("faxAjout").value = ""; dojo.byId("telephoneAjout").value = ""; ); dojo.event.topic.subscribe("/modif", function(data, type, request) if(type == "before") var id = data.split("_")[1]; var tr = dojo.byId("row_"+id); var tds = tr.getElementsByTagName("td"); dojo.byId("utilisateurid").value = id; dojo.byId("prenom").value = dojo.string.trim(dojo.dom.textContent(tds[0])); dojo.byId("nom").value = dojo.string.trim(dojo.dom.textContent(tds[1])); dojo.byId("email").value = dojo.string.trim(dojo.dom.textContent(tds[2])); dojo.byId("login").value = dojo.string.trim(dojo.dom.textContent(tds[3])); dojo.byId("password").value = dojo.string.trim(dojo.dom.textContent(tds[4])); dojo.byId("titre").value = dojo.string.trim(dojo.dom.textContent(tds[5])); dojo.byId("fax").value = dojo.string.trim(dojo.dom.textContent(tds[6])); dojo.byId("telephone").value = dojo.string.trim(dojo.dom.textContent(tds[7]));

- 49 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 238: Ejb Jsf Struts Flex Jasper

); </script>

L’interface d’administration des clients se présente sur une unique page qui est mise à jour de façon asynchrone lorsqu’un client est supprimé, modifié ou ajouté :

L’action AdministrationAction implémente trois interfaces :

L’interface com.opensymphony.xwork2.Preparable. Cette interface définit une méthode void prepare() qui est executée par le framework avant toute autre méthode de l’action. C’est en fait l’interceptor prepare qui appelle cette méthode void prepare(). C’est l’endroit idéal pour récupérer une référence du session bean UtilisateurDAOBean via la méthode void initialisation().

L’interface org.apache.struts2.interceptor.ServletRequestAware pour pouvoir accéder aux objets de la requête.

L’interface com.opensymphony.xwork2.ModelDriven. Dans ce cas l’interceptor modelDriven appelle la méthode getModel() pour mettre le résultat dans la pile, à savoir une instance de l’objet Utilisateur. Ce résultat est ensuite accessible avec le tag <s:property value="prenom" /> par exemple, à l’intérieur d’un tag iterator <s:iterator value="utilisateurs"> … </s:iterator>, dans la JSP listeClients.jsp.

Voici le code de l’action AdministrationAction.java :

package com.eni.dvtejb.clientStruts2.action; import java.math.BigDecimal; import java.util.List; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.apache.struts2.interceptor.ServletRequestAware; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.services.ServiceLocator; import com.eni.dvtejb.metier.sessions.PanierBeanRemote; import com.eni.dvtejb.metier.sessions.UtilisateurDAOBeanRemote; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.Preparable; public class AdministrationAction implements ServletRequestAware, Preparable, ModelDriven<Utilisateur>

- 50 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 239: Ejb Jsf Struts Flex Jasper

private static final Logger log = Logger.getLogger(AdministrationAction.class); private UtilisateurDAOBeanRemote utilisateurDAO; private List<Utilisateur> utilisateurs; private Utilisateur utilisateur; private Integer utilisateurid; private HttpSession mSession; private HttpServletRequest mRequest; public void setServletRequest(HttpServletRequest request) mRequest = request; mSession = mRequest.getSession(); @Override public Utilisateur getModel() return utilisateur; public AdministrationAction() public void initialisation() try UtilisateurDAOBeanRemote utilisateurDAO = (UtilisateurDAOBeanRemote) ServiceLocator.getInstance().getService("VenteEnLigne/UtilisateurDAOBea n/remote"); this.utilisateurDAO = utilisateurDAO; catch (NamingException e) e.printStackTrace(); public String execute() this.utilisateurs = utilisateurDAO.rechercherTous(); mRequest.setAttribute("utilisateur",utilisateur); return Action.SUCCESS; public String rechercheTous() this.utilisateurs = utilisateurDAO.rechercherTous(); return Action.SUCCESS; public String save() this.utilisateurDAO.sauver(utilisateur); return execute(); public String remove() long l = Long.valueOf(utilisateur.getUtilisateurid()).longValue(); utilisateurDAO.supprimer(l); return execute(); public List<Utilisateur> getUtilisateurs() return utilisateurs; public Integer getUtilisateurid() return utilisateurid; public void setUtilisateurid(Integer utilisateurid) this.utilisateurid = utilisateurid;

- 51 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 240: Ejb Jsf Struts Flex Jasper

public void prepare() throws Exception initialisation() ; if (utilisateurid != null) utilisateur = utilisateurDAO.rechercher(utilisateurid); mSession.setAttribute("utilisateur", utilisateur); else utilisateur = new Client(); public Utilisateur getUtilisateur() return utilisateur; public void setPerson(Utilisateur utilisateur) this.utilisateur = utilisateur;

Le wokflow est le même pour l’écran de gestion des articles. Le tag <sx:div> possède de nombreux attributs parmi lesquels startTimerListenTopics, stopTimerListenTopics et updateFreq. Ces attributs permettent de définir une fréquence de rafraîchissement du contenu du tag <sx:div>. C’est en fait un timer qui est déclenché / arrêté par un ou plusieurs topics. Le code suivant permet de déclencher un timer qui recharge la liste des articles toutes les 5 secondes et l’affiche momentanément en rouge. Il permet aussi d’arrêter le timer si l’utilisateur souhaite arrêter le rafraîchissement de la liste. Quand l’utilisateur arrive pour la 1ère fois sur la page, le chargement de la liste des articles prendra au moins 5 secondes dans la mesure où le timer est démarré automatiquement. Une image est affichée pendant la durée d’exécution de la requête.

<div style="width: 1400px;border-style: solid"> <div style="text-align: right;"> <sx:a notifyTopics="/rafraichir" >Rafraîchir la liste <img src="<%=request.getContextPath()%>/images/reload_page.png" ></sx:a> </div> <div style="text-align: right;"> <sx:a notifyTopics="/debutTimer" >Rafraîchir la liste toutes les 5 secondes<img src="<%=request.getContextPath()%>/images/reload_page.png" ></sx:a> </div> <div style="text-align: right;"> <sx:a notifyTopics="/stopTimer" >Arrêter le timer<img src="<%=request.getContextPath()%>/images/reload_page.png" ></sx:a> </div> <img id="rafraichissement" src="$pageContext.request.contextPath/images/rechargement.gif" style="display:none"/> <sx:div id="articles" href="%descrsUrl" loadingText="Chargement en cours ..." listenTopics="/rafraichir" showLoadingText="true" updateFreq="5000" highlightColor="red" startTimerListenTopics="/debutTimer" stopTimerListenTopics="/stopTimer" indicator="rafraichissement" /> </div>

Voici une capture d’écran lors de l’exécution de la requête :

- 52 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 241: Ejb Jsf Struts Flex Jasper

Et voici l’affichage momentané en rouge de la liste. Cette liste redevient progressivement blanche dès que la liste est affichée :

- 53 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrO7rI5lH82ICwA=-enidentnumber

Page 242: Ejb Jsf Struts Flex Jasper

Le framework JSF 2.0

Ce sous­chapitre présente les principaux concepts du framework JSF ainsi que les nouveautés du framework dans la version 2.0. Certaines de ces nouveautés sont mises en œuvre par la suite.

1. Présentation

JSF est un framework pour le développement d’applications Web qui est basé sur des composants dans la mesure où chaque vue correspond à une structure de composants UI (User Interface). Il implémente l’architecture MVC : le modèle est la couche de managed beans (beans managés), la vue est représentée par les pages XHTML ou JSP et le contrôleur est la servlet javax.faces.webapp.FacesServlet.

À la différence de Struts, qui implique d’écrire une action qui joue le rôle de contrôleur pour traiter la requête et la diriger vers une JSP, JSF renvoie automatiquement le contrôle vers la JSP.

Les objectifs du framework JSF sont de fournir aux développeurs des outils simples à utiliser et qui facilitent le travail récurrent du développement d’IHM (validation de formulaires, évènements asynchrones Ajax…). Il permet de développer des applications web avec des composants graphiques standards. Les pages JSP contiennent des tags (composants) JSF qui sont liés à des beans en charge des traitements.

Le fichier de configuration faces­config.xml décrit la logique de navigation entre les pages ainsi que la définition des managed beans.

Parmi les nouveautés introduites dans la version 2.0 de JSF, qui nécessite un container de Servlet 2.5, on peut noter les évolutions suivantes :

le fichier faces­config.xml devient optionnel ;

les pages Facelets XHTML deviennent le standard pour créer des pages JSF, au lieu d’utiliser des pages JSP ;

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 243: Ejb Jsf Struts Flex Jasper

le support natif d’Ajax ;

la déclaration des managed beans via des annotations ;

le support de la JSR 303 Bean Validation ;

la création de composants rendu plus facile.

2. Managed bean et portées

On peut injecter des instances EntityManager dans des beans JSF managés (managed beans) pour persister les données. Ce sont des POJO, il n’est pas nécessaire d’hériter des classes spéciales, il suffit de fournir un constructeur sans argument et d’ajouter les accesseurs (getters/setters). Leur cycle de vie est géré par le framework JSF. Les accès aux propriétés et aux méthodes d’un managed bean depuis une page sont fait avec des expressions du Langage d’Expression unifié EL (Expression Language).

JSF 2 introduit une spécification pour les annotations des Faces Managed Beans. Ainsi, l’annotation @javax.faces.bean.ManagedBean remplace la déclaration d’un managed bean dans le fichier faces­config.xml. Au démarrage de l’application, JSF scanne les classes qui portent cette annotation et les déclare en tant que managed beans. Le moment quand ces beans sont instanciés peut être contrôlé grâce à l’attribut eager. Sa valeur par défaut est false. Dans le cas d’une portée de type application, si la valeur est true, le bean sera instancié au moment où l’application démarre et non pas lors de la première référence au bean.

Un managed bean a une portée définie à l’aide d’annotations au niveau de la classe. Ces annotations sont :

@javax.faces.bean.RequestScoped : la portée est limitée à l’envoi d’une requête et la réception d’une réponse. Lors d’un forward vers une autre page, les données qui sont stockées dans cette portée restent accessibles. En revanche, elles sont perdues lors d’une redirection. Le bean est instancié à chaque nouvelle requête. C’est la portée par défaut.

@javax.faces.bean.SessionScoped : les données sont conservées au cours des échanges entre un même utilisateur. Le bean est instancié pour la durée de la session.

@javax.faces.bean.ViewScoped : les données sont accessibles au niveau de la vue (page) seulement. Cette portée existe tant que l’utilisateur reste sur la même page. Dès qu’il navigue vers une autre vue, les données de la page sont perdues, sachant qu’une vue peut s’étaler sur plusieurs requêtes.

@javax.faces.bean.CustomScoped : c’est une portée personnalisée.

@javax.faces.bean.NoneScoped : le managed bean n’a pas de portée et est instancié à chaque fois qu’il est référencé.

@javax.faces.bean.ApplicationScoped : les données restent accessibles dans toutes les pages d’une même application, pour tous les utilisateurs. Cette portée convient donc pour le partage de données.

Une autre portée existe, Flash, qui n’a pas d’annotation et qui apporte plus de granularité en fournissant la possibilité de sauvegarder un objet entre deux requêtes consécutives seulement, de sorte que cet objet continue de vivre après une redirection. Il est détruit ensuite.

La portée doit être choisie de façon adéquate dans la mesure où elle impacte les performances. Par exemple, assigner une portée de type session à un managed bean n’est pas conseillé s’il n’est utilisé que le temps d’une requête HTTP car d’une part il réside en mémoire inutilement et d’autre part le supprimer de la session à un coût en terme de mémoire également.

L’origine du mot bean est liée à celle du mot Java. En effet Java est synonyme de café (aux Etats­Unis). Le café est fait à partir de grains (beans) qui encapsulent son goût.

Les managed beans font usage de l’injection de dépendance pour accéder aux EJB. Concernant les conventions de nommage, il est d’usage de leur donner un nom qui se termine avec le suffixe « bean ».

Le schéma ci­dessous illustre la vision d’ensemble d’une application faisant usage de JSF pour la partie présentation, à l’intérieur du conteneur Web, et faisant usage des EJB pour la partie métier, à l’intérieur du conteneur EJB :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 244: Ejb Jsf Struts Flex Jasper

Dans JSF, l’expression backing bean est également utilisée. Un backing bean est un managed bean qui est dédié à une vue. C’est donc d’avantage un concept ou « design pattern » dans lequel un backing bean est le

« backing » code derrière une vue JSF. Il a généralement une portée de type requête et contient les données et la logique d’une seule vue.

3. Le langage d’expression unifié EL

Le langage d’expression unifié est le résultat de l’union du langage d’expression JSP et du langage d’expression JSF. Les expressions délimitées par #… sont utilisées dans les attributs des tags. Les possibilités offertes par ce langage d’expression sont multiples : accès en écriture/lecture aux propriétés des managed beans, appels des méthodes des managed beans (ces méthodes ne prennent pas de paramètres), accès aux attributs dans la session, à des éléments de Map ou de tableaux. Il permet aussi l’utilisation d’opérateurs logiques tels que empty, eq, ne, gt, lt, le, not, and, or.

Voici un aperçu des possibilités offertes :

action="#loginBean.verifierLogin".

Cette expression appelle la méthode verifierLogin() du managed bean LoginBean. La String retournée par la méthode détermine la navigation.

value="#(magasinBean.montantTotal * 1.19) + 4.50".

Dans un tag <h:outputLabel> par exemple, cette expression calcule le montant total de la commande, toutes taxes comprises, ainsi que les frais de transport. C’est une expression à utiliser avec parcimonie dans la mesure où les calculs doivent être calculés dans la couche métier.

value="#rechercheJSFBean.catalogueCritere".

Cette expression lie la valeur de la propriété catalogueCritere du managed bean RechercheJSFBean. Lors de la phase de restitution de la vue, la méthode getCatalogueCritere() du managed bean est appelée et lors de la phase de mise à jour des valeurs du modèle, la méthode setCatalogueCritere() est appelée.

rendered="#not empty magasinBean.articlesPanier".

Cette expression définit un booléen (vrai/faux) comme valeur dans l’attribut rendered d’un composant.

value=" Votre mot de passe est #loginBean.password".

La combinaison d’une String avec une expression EL est possible.

value="#rechercheJSFBean.nomsProduitsListe[2]".

Cette expression retourne l’élément à l’indice 2 de la liste nomsProduitsListe.

action="#rechercheJSFBean.rechercheArtParCatalogue(’3’)".

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 245: Ejb Jsf Struts Flex Jasper

Depuis Java EE 6, il est possible de passer des paramètres directement à l’intérieur d’une expression.

4. Les phases du cycle de vie

Il est utile de connaître et de comprendre le cycle de vie JSF pour développer des applications. JSF utilise un cycle de vie pour analyser une requête, organiser et traiter ses composants et le modèle sous­jacent, et envoyer une réponse. Ce cycle de vie contient six phases bien définies. Ces phases sont associées à des ID définis dans la classe javax.faces.event.PhaseId. Chaque fois qu’un utilisateur envoie une requête à une application Web JSF, une ou plusieurs de ces phases seront appelées dans l’ordre indiqué sur le schéma suivant :

Le cycle de vie d’une requête. En gras les IDs des phases.

La requête ne passe pas nécessairement par toutes les phases. Cela dépend du type de la requête, des erreurs de validation et de conversion rencontrées, ainsi que du type de réponse. Voici une description sommaire des étapes :

1 ­ Restitution de la vue : construction et mise en mémoire de la structure des composants de la page.

2 ­ Application des paramètres de la requête : les paramètres de la requête sont récupérés et assignés aux composants de la page.

3 ­ Validations : vérification des valeurs des paramètres d’entrée.

4 ­ Mise à jour des valeurs du modèle : les valeurs des paramètres sont chargées dans les managed beans.

5 ­ Invocation de l’application : les actions et les évènements sollicités sont exécutés, ainsi que la navigation s’il y a lieu.

6 ­ Restitution de la réponse : envoi de la page à l’utilisateur avec actualisation de ses éléments et valeurs.

Dans le fichier de description faces­config.xml, les balises <lifecycle> et <phase­listener> fournissent la possibilité d’agir avant et après les phases de ce cycle de vie par l’intermédiaire de listeners qui sont à l’écoute d’évènements. Un listener de phases peut être défini de façon globale c’est­à­dire qu’il sera exécuté pour toutes les requêtes. Pour faciliter la détection de bugs et la compréhension de l’exécution des requêtes, la mise en place d’un tel listener est conseillée dès le début du développement d’une application basée sur JSF.

Voici le code du listener de phases MonPhaseListener.java, dont le rôle est d’afficher des messages dans les logs lors des changements de phase :

package com.eni.dvtejb.clientJSF2.listeners; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener;

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 246: Ejb Jsf Struts Flex Jasper

public class MonPhaseListener implements PhaseListener private static final long serialVersionUID = 1L; public MonPhaseListener() public void beforePhase(PhaseEvent pe) if (pe.getPhaseId() == PhaseId.RESTORE_VIEW) System.out.println("Nouvelle requête en cours ..."); System.out.println("avant - " + pe.getPhaseId().toString()); public void afterPhase(PhaseEvent pe) System.out.println("après - " + pe.getPhaseId().toString()); if (pe.getPhaseId() == PhaseId.RENDER_RESPONSE) System.out.println("Fin d’analyse de la requête.\n"); public PhaseId getPhaseId() return PhaseId.ANY_PHASE;

Le listener doit être déclaré dans le fichier faces­config.xml :

<lifecycle> <phase-listener>com.eni.dvtejb.clientJSF2.listeners.MonPhaseListener </phase-listener> </lifecycle>

5. La navigation

La navigation, c’est­à­dire l’enchaînement des pages XHTML, a été retravaillé dans JSF 2. Dans JSF 1.2 les règles de navigation étaient placées dans le fichier de configuration faces­config.xml. Ceci est toujours possible dans JSF 2.0 mais la navigation peut maintenant être également déterminée de façon implicite ou de façon conditionnelle. Cependant la navigation explicite, via le fichier faces­config.xml, continue à être parfois utilisée. Le fichier faces­config.xml reste obligatoire, même s’il n’y a pas de navigation définie à l’intérieur. Lorsque la navigation est définie à l’intérieur du fichier, la syntaxe est similaire à la suivante :

<navigation-rule> <from-view-id>/login/login.xhtml</from-view-id> <navigation-case> <from-outcome>connecte</from-outcome> <to-view-id>/menu/accueil.xhtml</to-view-id> </navigation-case> <navigation-case> <from-outcome>nonconnecte</from-outcome> <to-view-id>/login/login.xhtml</to-view-id> </navigation-case> </navigation-rule>

Il est maintenant possible d’utiliser le langage d’expression dans les règles de navigation du fichier.

Si la navigation n’est pas définie dans ce fichier, la navigation est alors implicite : la chaîne de type String retournée par une méthode d’un managed bean correspond au nom d’un fichier XHTML.

Dans le cas où les pages XHTML sont définies dans différents répertoires, il faut spécifier le nom du répertoire dans la String de retour de la méthode. Par exemple la méthode suivante dirige le client vers la page afficherDetails.xhtml qui se trouve dans le répertoire magasin :

public String afficherDetails(PhaseEvent event) ... return "/magasin/afficherDetails";

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 247: Ejb Jsf Struts Flex Jasper

6. Validation et conversion

La validation est importante. Elle n’autorise pas la forme à être soumise s’il y a une erreur. JSF contient des validateurs par défaut pour vérifier la présence, la longueur et le format (<f:validateRequired>, <f:validateLength>, <f:validateDoubleRange>, <f:validateLongRange>) des valeurs saisies dans les champs des formulaires.

Quand les validateurs par défaut ne suffisent pas, JSF propose de développer ses propres validateurs :

Le validateur peut être spécifique à une application et dans ce cas le développement d’une méthode de validation dans un managed bean peut suffire. L’appel de cette méthode de validation dans un composant <h:inputText> se fait alors dans l’attribut validator :

<h:inputText id="login" value="#leClient.login" validator="#clientBean.validerNom" />

Ou bien le validateur peut être générique et donc être utilisé dans plusieurs applications auquel cas il faut créer une classe qui implémente l’interface javax.Faces.Validator.

Une nouvelle annotation, @FacesValidator, facilite sa création, comme nous le verrons par la suite.

JSF fournit des convertisseurs standards très pratiques pour convertir un nombre (<f:convertNumber>) ou une date (<f:convertDateTime>).

À l’instar des validateurs, si les convertisseurs par défaut ne suffisent pas, il est alors nécessaire de développer ses propres convertisseurs. Là encore le développement est facilité grâce à une nouvelle annotation : @FacesConverter.

Il est possible de passer outre les étapes de validation et de conversion du cycle de vie en affectant la valeur true à l’attribut immediate du composant qui déclenche l’action.

7. Évènements et listeners d’évènements

JSF offre la possibilité d’émettre des évènements et de les écouter par l’intermédiaire des listeners. On distingue quatre types d’évènements :

Les évènements de phases qui sont déclenchés avant et après chaque phase.

Les évènements d’applications (par exemple un clic sur un bouton).

Les évènements suite à des changements de valeurs des composants.

Les évènements systèmes.

Les évènements systèmes sont une des nouveautés de JSF 2. Un objet qui implémente l’interface javax.faces.event.SystemEventListener se met à l’écoute d’évènements systèmes. Un composant peut être à l’écoute d’un évènement système en utilisant le nouveau tag <f:event>.

Tous ces évènements sont gérés par des listeners d’évènements. Les classes des listeners implémentent l’interface javax.faces.event.PhaseListener.

8. Liste des tags

La liste des tags JSF 2.0 est divisée en quatre librairies de tags (taglibs) essentiellement : les core tags (généralement préfixés avec la lettre f), les HTML tags (avec le préfixe h), les Facelets tags (avec le préfixe ui) et les Composite tags (avec le préfixe composite). Les principaux tags de ces librairies sont listés dans le tableau suivant :

Validation

Conversion

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 248: Ejb Jsf Struts Flex Jasper

Librairies de tags Tags

Préfixe usuel Namespace / URI

f http://java.sun.com/jsf/core f:view, f:facet, f:attribute, f:param, f:actionListener, f:valueChangeListener, f:event, f:converter, f:convertDateTime, f:convertNumber, f:validator, f:validateDoubleRange, f:validateLongRange, f:validateLength, f:validateRequired, f:validateBean, f:validateRegex, f:loadBundle, f:selectedItem, f:selectItems, f:ajax, f:viewParam, f:metadata, f:verbatim

h http://java.sun.com/jsf/html h:head, h:body, h:form, h:outputStylesheet, h:outputScript, h:inputText, h:inputTextArea, h:inputSecret, h:inputHidden, h:outputLabel, h:outputLink, h:outputFormat, h:outputText, h:commandButton, h:button, h:commandLink, h:link, h:message, h:messages, h:graphicImage, h:selectOneListbox, h:selectOneMenu, h:selectOneRadio, h:selectBooleanCheckBox, h:selectManyCheckBox, h:selectManyListbox, h:selectManyMenu, h:panelGrid, h:panelGroup, h:dataTable, h:column

ui http://java.sun.com/jsf/facelets ui:define, ui:insert, ui:composition, ui:component, ui:decorate, ui:fragment, ui:include, ui:param, ui:repeat, ui:debug, ui:remove

composite http://java.sun.com/jsf/composite composite:interface, composite:implementation, composite:attribute, composite:facet, composite:renderFacet, composite:insertChildren, composite:valueHolder, composite:editableValueHolder, composite:actionSource, composite:extension, composite:insertFacet

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrlFltVlH82ICwA=-enidentnumber

Page 249: Ejb Jsf Struts Flex Jasper

Développement de l’application cliente avec JSF 2.0

Le framework JSF est utilisé pour interagir avec la partie back­end que sont les services EJB dont certains ont été développés dans les chapitres précédents.

1. Création du projet

Il existe plusieurs implémentations de JSF. Les principales sont Mojarra de Sun et MyFaces d’Apache. JBoss intègre la version Mojarra de JSF. Depuis la version 5.1 du serveur JBoss, la version des librairies JSF incluses dans le serveur est la 2.0. Il n’est donc pas nécessaire de configurer le projet pour y ajouter des librairies. Les librairies incluses sont les suivantes :

jboss­6.0.0.M1\server\web\deploy\jbossweb.sar\jsf­libs\ jsf­api.jar ;

jboss­6.0.0.M1\server\web\deploy\jbossweb.sar\jsf­libs\ jboss­faces.jar ;

jboss­6.0.0.M1\server\web\deploy\jbossweb.sar\jsf­libs\ jsf­impl.jar (contient le framework de templates facelets, entre autres).

Concernant le framework de template Facelets, qui sera vu plus loin dans ce chapitre, depuis la version 2, il n’est plus nécessaire d’installer la librairie jsf­facelets.jar. Pareillement, il n’est plus nécessaire d’inclure de librairies JSTL.

JSF requiert l’API Java Standard Tag Library (JSTL) 1.2. C’est un ensemble de tags personnalisés, développé dans le cadre de la JSR 052 et qui offre des fonctionnalités souvent rencontrées dans les JSP telles que les tags de structure (conditionnement, itération…), l’utilisation de document XML, l’exécution de requêtes SQL, ou encore l’internationalisation. De plus JSTL offre un langage constitué d’expressions pour accéder et utiliser des objets Java depuis les différentes portées (page, request, session, application) de la page JSP. La syntaxe est $xxx où xxx désigne une variable d’un objet Java présent dans une portée particulière.

Création du projet web dynamique

Créez un projet web dynamique dans Eclipse. La procédure de création du projet est identique au chapitre précédent. Nommez ce projet VenteEnLigneWebJSF2. Ajoutez­le dans l’espace de développement EnvironnementEJB3 pour qu’il soit visible dans Eclipse.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 250: Ejb Jsf Struts Flex Jasper

Configuration du projet web dynamique

Pour la manipulation des fichiers XHTML, l’installation de JBoss Tools dans Eclipse est recommandée. JBoss Tools est une suite de plugins, dont un plugin qui facilite l’édition de fichiers XHTML. Ce plugin permet, entre autres, d’avoir un aperçu du rendu du fichier XHTML, de disposer d’une palette de tags sous forme de vue, de filtrer les valeurs des fichiers de properties et offre la complétion sur les tags JSF, ce que ne permet pas WTP par défaut. Reportez­vous au chapitre Mise en place de l’environnement pour l’installation de ce plugin dans Eclipse.

Plugin JBoss Tools intégré à Eclipse :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 251: Ejb Jsf Struts Flex Jasper

Le projet VenteEnLigneWebJSF2 a été ajouté dans les références du projet VenteEnLigne.

Le projet a également été ajouté dans les dépendances du module Java EE, au niveau des propriétés du projet VenteEnLigne :

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 252: Ejb Jsf Struts Flex Jasper

Dans les propriétés du projet VenteEnLigneWebJSF2, cochez la case qui correspond au JAR/Module VenteEnLigneEJB.jar pour pouvoir utiliser les EJB :

Modifiez le fichier de configuration web.xml pour que les URLs se terminant par JSF soient gérées par la servlet javax.faces.webapp.FacesServlet. Voici un extrait du fichier web.xml modifié :

<context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name>

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 253: Ejb Jsf Struts Flex Jasper

<param-value>.xhtml</param-value> </context-param> <!-- Mode Development: permet d’afficher des warnings et des messages d’erreurs détaillés --> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping>

Le paramètre PROJECT_STAGE est une des nouveautés de la version 2.0 de JSF. L’énumération javax.faces.application.ProjectStage contient une liste de valeurs qui correspondent à différents niveaux d’un cycle de développement. Ces valeurs sont :

Development

UnitTest

SystemTest

Production

Concrètement, ce paramétrage permet de contrôler la quantité et les types de messages que l’on souhaite afficher. La modification de ce paramètre nécessite de redémarrer le serveur pour qu’elle soit prise en compte. Le niveau peut être récupéré dans l’application avec la méthode getProjectStage() de la classe javax.faces.application.Application :

FacesContext context = FacesContext.getCurrentInstance(); Application app = context.getApplication(); if (app.getProjectStage() == ProjectStage.Production) ...

De plus, la classe javax.faces.context.FacesContext possède une méthode isProjectStage(ProjectStage stage) qui renvoie vrai si le niveau dans le cycle de développement est égal à celui passé en paramètre.

La configuration du projet VenteEnLigneWebJSF2 est maintenant terminée.

2. Layout commun avec Facelets

Pour créer un layout commun aux pages de l’application, JSF 2 propose le développement de templates avec le framework Facelets qui contient des similarités avec Tiles, souvent utilisé avec Struts.

Le framework Facelets est hébergé à l’adresse http://facelets.dev.java.net/ et a été créé par Jacob Hookom. Ce framework est devenu un standard dans JSF 2. Les pages sont au format XHTML et remplacent les pages JSP qui restent cependant utilisables. L’un des inconvénients des JSP est la possibilité d’introduire du code Java, ce qui d’une certaine manière peut être vue de façon négative dans la mesure où le développeur a donc plus de chances de commettre des erreurs dans le code. Alors qu’il n’est pas possible de mettre du code Java dans une Facelet (c’est­à­dire une page XHTML). XHTML est de l’HTML bien formé, selon les règles de XML.

En ce qui concerne la version 2.2 des JSP, qui correspond à la JSR­245 et qui fait partie de Java EE 6, JSF 2 ne supporte pas encore toutes les fonctionnalités de cette version.

Les Facelets prendront en charge les phases de « Restitution de la vue » et de « Restitution de la réponse ».

La mise en œuvre d’un layout commun exige dans un premier temps de modifier le fichier web.xml pour préciser que les templates des vues sont écrites en XHTML et que leur suffixe est donc .xhtml :

<context-param>

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 254: Ejb Jsf Struts Flex Jasper

<param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param>

Le layout commun est défini dans le fichier template.xhtml. Sa représentation graphique se présente sous la forme suivante :

Dans Eclipse, créez un répertoire WebContent/templates, à la racine du projet VenteEnLigneWebJSF2. Ce répertoire servira à stocker les templates. Voici le code de la template, template.xhtml, utilisée dans l’application :

<!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:ui="http://java.sun.com/jsf/facelets"> <ui:debug /> <h:head> <title><ui:insert name="titre">Titre</ui:insert></title> <link rel="stylesheet" type="text/css" href="../css/styles.css"/> </h:head> <h:body> <table width="100%" height="100%" border="1"> <tr height="5%" > <td colspan="2"> <center> <ui:include src="header.xhtml"/></center> </td> </tr> <tr height="90%" > <td width="80" bgcolor="#00CE67"> <ui:include src="menu.xhtml"/> </td> <td> <div id="contenu"> <ui:insert name="contenu"> </ui:insert> </div> </td> </tr> <tr height="5%"> <td colspan="2"> <center><ui:include src="footer.xhtml"/></center> </td> </tr> </table> </h:body> </html>

Dans une page qui utilise cette template, l’attribut name du tag <ui:define> doit correspondre à l’attribut name du

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 255: Ejb Jsf Struts Flex Jasper

tag <ui:insert> pour désigner le nom d’un élément à insérer dans une template. L’ajout du tag <ui:debug /> permet d’afficher une fenêtre de debug des Facelets lorsque l’on presse les touches [Ctrl] [Shift] D :

Créez ensuite les fichiers menu.xhtml, header.xhtml, footer.xhtml. Les vues représentent tous les composants d’une page et doivent être définies à l’intérieur du tag <ui:composition> pour indiquer que leurs contenus doivent être utilisés dans d’autres Facelets (fichiers XHTML), dans ce cas le fichier template.xhtml :

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <ui:composition> <f:view> ... </f:view> </ui:composition> </html>

Puis pour utiliser la template dans une page, le tag <ui:composition> est utilisé de nouveau mais avec l’attribut template pour préciser l’emplacement et le nom du fichier de la template. La page accueil.xml suivante utilise le template définit précédemment :

<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" template="/templates/template.xhtml"> <ui:define name="titre"> <h:outputText value="#lesMessages.accueil_titre" /> </ui:define> <ui:define name="contenu">

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 256: Ejb Jsf Struts Flex Jasper

<p/> <f:view > <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <center> <h2> <h:outputText value="#lesMessages.accueil_titre" /> </h2> Liens ... </center> <br /> <div align="right" > <h:graphicImage url="/images/utilisateur.gif" /><br /> <h:outputText value="#lesMessages.general_nom" /> : <h:outputText value="#sessionScope.loginBean.client.nom"/><br/> <h:outputText value="#lesMessages.general_prenom" />: <h:outputText value="#sessionScope.loginBean.client.prenom"/> </div> </f:view> </ui:define> </ui:composition>

Le tag <ui:define> permet de passer des arguments (titre, contenu de la page) à la template via le tag <ui:insert> correspondant qui se trouve dans cette template.

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 257: Ejb Jsf Struts Flex Jasper

Concernant les commentaires à l’intérieur des fichiers XHTML, si vous utilisez le tag < !­ ­ … ­ ­>, seul le code HTML sera ignoré. Pour forcer les facelets à ignorer le code à l’intérieur du tag < !­ ­ … ­ ­>, vous devez ajouter cette ligne dans le fichier web.xml :

<context-param> <param-name>facelets.SKIP_COMMENTS</param-name> <param-value>true</param-value> </context-param>

Une alternative pour mettre des lignes en commentaire dans les facelets est de les encadrer avec le tag <ui:remove>.

3. Gestion de la page de login

La structure de la page index est similaire à celle développée dans le projet utilisant Struts 2.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 258: Ejb Jsf Struts Flex Jasper

Il est souhaitable de créer deux managed beans : un managed bean pour gérer l’authentification et un managed bean pour gérer l’inscription d’un nouvel utilisateur.

Pour accéder au chemin du contexte (context path), il faut passer par l’expression request.contextPath. L’objet request est un des objets implicites, au niveau de la portée request, qui est fourni par JSF. Les autres objets implicites de portée request sont : cookie, facesContext, header, headerValues, param, paramValues, request, requestScope, view.

Le managed bean LoginBean fait appel au stateful session bean FacadeBean pour vérifier le login et le mot de passe. Il n’y a pas besoin de faire de lookup JNDI, les EJB peuvent être injectés dans des managed beans.

JBoss impose cependant d’utiliser l’attribut name de l’annotation @EJB. Voici un extrait du code du managed bean LoginBean.java :

package com.eni.dvtejb.clientJSF2.beans; import java.util.Locale; import java.util.Map; import javax.ejb.EJB; import javax.faces.application.FacesMessage; import javax.faces.bean.SessionScoped; import javax.faces.bean.ManagedBean; import javax.faces.context.FacesContext; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.sessions.FacadeRemote; @ManagedBean @SessionScoped public class LoginBean private static final Logger log = Logger.getLogger(LoginBean.class); // constructeur publique sans argument public LoginBean() private String login; private String password; public Client getClient()

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 259: Ejb Jsf Struts Flex Jasper

return client; public void setClient(Client client) this.client = client; private Client client; public String getLogin() return login; public void setLogin(String login) this.login = login; public String getPassword() return password; public void setPassword(String password) this.password = password; private Locale locale = Locale.getDefault(); public Locale getLocale() return locale; public void setLocale(Locale newValue) locale = newValue; // Injection d’EJB dans le Managed bean @EJB(name="VenteEnLigne/FacadeBean/remote") private FacadeRemote facade; public static void addErrorMessage(String msg) FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg); FacesContext fc = FacesContext.getCurrentInstance(); fc.addMessage(null, facesMsg); public String verifierLogin() throws Exception log.info("Appel de la méthode verifierLogin()"); Utilisateur utilisateur = facade.findUtilisateurByLoginPwd(login, password); client = (Client)utilisateur; if (null == client) addErrorMessage("Mauvais login et/ou mauvais mot de passe, veuillez réessayer."); return "login"; else return "/principal/accueil"; public String deconnexion() log.info("Appel de la méthode deconnexion()"); FacesContext context = FacesContext.getCurrentInstance(); HttpSession session = (HttpSession) context.getExternalContext().getSession(true); if (null == session) log.info("Cas invalide"); return "invalide"; else log.info("Cas valide");

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 260: Ejb Jsf Struts Flex Jasper

session.invalidate(); login = null; password = null; return "/index"; public String changerLangue() log.info("Appel de la méthode changerLangue()"); FacesContext context = FacesContext.getCurrentInstance(); // Récupération du paramètre langue Map map = context.getExternalContext().getRequestParameterMap(); String langue = (String) map.get("langue"); if ("fr".equals(langue)) log.info("CAS FR"); context.getViewRoot().setLocale(Locale.FRENCH); this.locale = Locale.FRENCH; else log.info("CAS EN"); context.getViewRoot().setLocale(Locale.UK); this.locale = Locale.UK; context.getApplication().setDefaultLocale(context.getViewRoot().getL ocale()); return "index";

La vue courante est accessible grâce à la classe FacesContext et en particulier la méthode getViewRoot(). Le bean LoginBean est déclaré managé avec l’annotation @javax.faces.bean.ManagedBean.

Cette annotation évite d’avoir à déclarer ce bean, comme c’était le cas dans la version 1.2, dans le fichier faces­config.xml de la manière suivante :

<managed-bean> <managed-bean-name>loginBean</managed-bean-name> <managed-bean- class>com.eni.dvtejb.clientJSF2.LoginBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>

Créez une page JSP login.xhtml dans le répertoire WebContent/login/ :

<?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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui=" http://java.sun.com/jsf/Facelets"> <head> <title><h:outputText value="#lesMessages.login_titre"/></title> <link rel="stylesheet" type="text/css" href="../css/styles.css"/> </head> <f:view locale="#loginBean.locale"> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form> <body> <center> <br /><br /><br /><br /> <h:panelGrid columns="2" id="panel" border="1" bgcolor="#CECEFF">

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 261: Ejb Jsf Struts Flex Jasper

<f:facet name="header"> <h:outputText value="#lesMessages.login_label3"/> </f:facet> <h:outputLabel for="login" value="#lesMessages.login_label1" /> <h:inputText id="login" value="#loginBean.login" required="true" /> <h:outputLabel for="password" value="#lesMessages.login_label2" /> <h:inputSecret id="password" value="#loginBean.password" required="true" /> <f:facet name="footer"> <h:panelGroup style="display:block; text-align:center"> <h:commandButton id="submit" value="#lesMessages.login_label4" action="#loginBean.verifierLogin" /> </h:panelGroup> </f:facet> </h:panelGrid> <h:messages /> </center> </body> </h:form> </f:view> </html>

L’attribut xmlns du tag <HTML> veut dire XML Namespace. C’est un espace de nom qui indique où se trouvent les taglibs Core, HTML et Facelets. Le choix des préfixes (h, f, ui) est libre.

Le tag <f:view> est un tag nécessaire pour indiquer le début d’un ensemble de tags JSF dans une vue de type JSP. Il est dans ce cas optionnel puisque la vue utilise les Facelets.

Pour lier les composants JSF avec le managed bean LoginBean, on utilise des expressions de la syntaxe EL (Expression Language) à l’intérieur des délimiteurs #…. Une expression est constituée par le nom du managed bean suivi d’un point puis du nom de la propriété désirée.

Le rendu des accents est possible avec un encodage de caractères qui suit la norme ISO­8859­1 (Latin­1). Cette norme prend en charge les langues européennes latines comme le français.

Le tag <h:outputLabel> affiche un label pour le composant auquel il est associé via l’attribut for. La valeur de cet attribut correspond à l’id du composant.

Le tag <h:inputText> affiche un champ HTML INPUT de type text. L’attribut required rend obligatoire la saisie d’une valeur dans ce champ.

Le tag <h:inputSecret> masque le contenu du champ de sorte que le mot de passe saisi par l’utilisateur soit masqué.

Voici les composants de la page de login, avec leurs représentations graphiques :

En cas de connexion réussie, l’utilisateur est dirigé vers la page d’accueil, accueil.xhtml. Il a ainsi également accès au

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 262: Ejb Jsf Struts Flex Jasper

menu qui permet l’édition du profil, la recherche et la commande des articles, ainsi que la visualisation de l’historique des commandes.

Les variables du bean managé sont sauvées dans le scope Session et sont donc ainsi accessibles depuis toutes les pages pendant la durée de la session. Ceci permet l’affichage des nom et prénom de l’utilisateur connecté dans toutes les pages, en passant directement par la variable client du bean LoginBean.

En cas de mauvaise connexion, un message d’erreur est affiché et l’utilisateur reste sur la page login.xhtml.

Concernant la validation des données entrées, le tag <h:message /> affiche les messages d’erreurs. Dans le bean, la classe javax.faces.application.FacesMessage encapsule les messages selon plusieurs niveaux d’erreurs. Les messages affichés sont récupérés à partir du contexte. Pour ajouter des messages dans le contexte, il suffit de les passer en paramètre de la méthode addMessage(String clientId, FacesMessage message) de la classe javax.faces.context.FacesContext.

La navigation est implicite puisqu’elle est déterminée à partir des valeurs de retour des méthodes du bean. Cependant elle peut être décrite dans le fichier de configuration faces­config.xml à l’aide d’un modèle de navigation basé sur des règles, comme mentionné dans le paragraphe Les phases du cycle de vie :

<navigation-rule> <from-view-id>/login/login.xhtml</from-view-id> <navigation-case> <from-outcome>accueil</from-outcome> <to-view-id>/principal/accueil.xhtml</to-view-id> </navigation-case> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/login/login.xhtml</to-view-id> </navigation-case> </navigation-rule>

Chaque tag <navigation­rule> décrit une règle qui spécife la page à afficher (tag <to­view­id) lorsque l’utilisateur clique sur un composant, tel qu’un bouton ou un lien, depuis une autre page (tag <from­view­id>). Ces règles peuvent s’appliquer à différents cas (tag <navigation­case>). Il existe des tags optionnels pour affiner ces règles : le tag <from­action> indique l’action à laquelle la règle s’applique et le tag <redirect /> indique que la réponse se fera sous forme de redirection.

Le lien de deconnexion dans le menu est géré avec la méthode deconnexion(). Elle récupère la session à partir du context et appelle la méthode invalidate() de la classe HttpSession puis redirige le client vers la page index.

La documentation de référence de JavaServer Faces comprenant l’ensemble des APIs est disponible sur le site de Sun à l’URL suivante : http://java.sun.com/javaee/javaserverfaces/reference/api/index.html

4. Internationalisation

L’internationalisation dans JSF est gérée de façon similaire à Struts. Elle passe par des fichiers de properties qui sont nommés de façon appropriée, c’est­à­dire avec un nom qui se termine par le code de la langue. Ces fichiers sont

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 263: Ejb Jsf Struts Flex Jasper

constitués de lignes qui sont au format clé=valeur. Pour l’application, deux langues ont été considérées : le français (fichier messages_fr_FR.properties) et l’anglais (fichier messages_en_GB.properties). Voici des extraits de ces deux fichiers qui sont placés dans le package com.eni.dvtejb.clientJSF2 :

Extrait du fichier messages_en_GB.properties :

# labels INDEX titre=Online shop label_connexion = Log in label_inscrire= Register langueFR=French langueEN=English

Extrait du fichier messages_fr_FR.properties :

# labels INDEX titre=Vente en ligne label_connexion = Se connecter label_inscrire= S’inscrire langueFR=Français langueEN=Anglais

Il faut déclarer les locales, c’est­à­dire les langues disponibles, dans le fichier faces­config.xml entre les tags <locale­config> et </locale­config> :

<application> <locale-config> <default-locale>fr_FR</default-locale> <supported-locale>en_GB</supported-locale> </locale-config> </application>

La locale qui sera considérée est celle qui est configurée au niveau des options du navigateur. La langue par défaut est le français. Ensuite il suffit de charger les messages en ajoutant le tag <f:loadbundle> dans le fichier .xhtml :

<f:loadBundle basename="com.eni.dvtejb.clientJSF.messages" var="lesMessages"/>

Puis de s’en servir, comme dans l’exemple suivant, qui est extrait de la page index.xhtml :

<h:outputLink value="login/login.jsf"><h:outputText value="#lesMessages.label_connexion"/></h:outputLink> | <h:outputLink value="inscription/inscription.jsf"><h:outputText value="#lesMessages.label_inscrire"/></h:outputLink>

Pour changer de langue dans toute l’application, il est possible de modifier de façon dynamique la locale :

public String changerLangue() System.out.println("Appel de la méthode changerLangue()"); FacesContext context = FacesContext.getCurrentInstance(); // Récupération du paramètre langue Map map = context.getExternalContext().getRequestParameterMap(); String langue = (String) map.get("langue"); if ("fr".equals(langue)) System.out.println("CAS FR"); context.getViewRoot().setLocale(Locale.FRENCH); this.locale = Locale.FRENCH; else System.out.println("CAS EN"); context.getViewRoot().setLocale(Locale.UK); this.locale = Locale.UK; System.out.println("La locale vaut: " + context.getViewRoot().getLocale());

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 264: Ejb Jsf Struts Flex Jasper

context.getApplication().setDefaultLocale(context.getViewRoot().getL ocale()); return "langueOK";

Le choix de la langue peut se faire dès la première page par exemple, avec l’utilisation du tag <f:param> qui permet de passer un paramètre langue à la méthode changerLangue :

Extrait du fichier index.xhtml :

<h:panelGroup> <h:commandLink value="#lesMessages.langueFR" action="#loginBean.changerLangue" style="font-family:arial, verdana;font-size:12px;"> <f:param name="langue" value="fr"/> <h:graphicImage value="/images/FR_drapeau.gif"/> </h:commandLink> <h:commandLink value="#lesMessages.langueEN" action="#loginBean.changerLangue" style="font-family:arial, verdana;font-size:12px;"> <f:param name="langue" value="en"/> <h:graphicImage value="/images/EN_drapeau.gif"/> </h:commandLink> </h:panelGroup>

Cependant, à ce niveau, seule la langue de la page index.xhtml est correctement modifiée. Pour modifier les pages suivantes, il est nécessaire de maintenir dans un managed bean de scope/portée session une variable qui stocke la Locale courante et qui est gérée dans la méthode changerLangue().

private Locale locale = Locale.getDefault(); public Locale getLocale() return locale; public void setLocale(Locale newValue) locale = newValue;

L’attribut Locale du tag <f:view> permet ensuite de propager dans les pages XHTML la Locale choisie :

<f:view locale="#loginBean.locale">

Une fois connectée, la langue peut être changée à partir d’une drop down box dans le menu de gauche :

// Gestion de la langue à partir de la drop down box public void changerLangueComboBox(ValueChangeEvent event) FacesContext context = FacesContext.getCurrentInstance(); if ("en".equals((String) event.getNewValue())) context.getViewRoot().setLocale(Locale.UK); this.locale = Locale.UK; else if ("fr".equals((String) event.getNewValue())) context.getViewRoot().setLocale(Locale.FRENCH); this.locale = Locale.FRENCH;

Les messages peuvent contenir des variables qui sont représentées par des numéros à l’intérieur d’accolades. Par exemple:

listeArticles_voici=Voici les 0 articles en magasin

Ensuite, pour fournir des valeurs à ces variables, vous pouvez utiliser les tags <h:outputFormat> et <f:param> :

<h:outputFormat value="#lesMessages.listeArticles_voici"> <f:param value="#magasinBean.pagination.nombreArticles"/> </h:outputFormat>

5. Affichage de la liste paginée des articles avec l’élément UI DataTable

L’affichage de la liste des articles est géré via le tag <h:dataTable> de la bibliothèque de tags JSF HTML. Ce tag

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 265: Ejb Jsf Struts Flex Jasper

génère une table et contient un ou plusieurs tags <h:column> qui définissent les colonnes de la table. Le style de la table peut être spécifié avec des classes CSS. Le rendu d’une colonne est un tag HTML <td>. Le body d’un tag <h:dataTable> peut contenir des facets <f:facet name="header"> et <f:facet name="footer">. Le rendu de la facet header est un tag <th> dans une ligne en haut de la table et le rendu de la facet footer est un tag <td> dans une ligne en bas de la table.

L’origine des données qui vont servir à alimenter la table générée est spécifiée dans l’attribut value. La valeur de cet attribut est une expression EL qui correspond à une instance de java.util.List ou un tableau d’objets dont les propriétés représentent les colonnes. Cela peut être un objet de type java.sql.ResultSet, java.util.ArrayList ou bien le type de données JSF javax.faces.model.DataModel.

Le composant dataTable itère sur la liste passée en paramètre de l’attribut value. La méthode getter get<NomListe> est appelée. En effet dans JSF les accès aux champs des managed beans se font toujours à travers les getters (également appelés accesseurs) et les setters (également appelés mutateurs). Au cours de l’itération, l’élément courant est stocké dans une variable temporaire dont le nom est spécifié par l’attribut var. Les contenus (par exemple <h:outputText>) des colonnes <h:column> sont répétés dans des lignes, sauf le contenu du composant <f:facet> qui peut servir à indiquer les titres des colonnes.

La classe abstraite javax.faces.model.DataModel est le modèle pour la table. Elle contient des propriétés utiles qui facilitent la manipulation des données de la table. Encapsuler la liste dans un DataModel permet de récupérer la ligne qui a été sélectionnée, le nombre de lignes et les données d’une ligne. La représentation des données dans un DataModel est prise en charge par la méthode setWrappedData(java.lang.Object objet). La classe javax.faces.model.ListDataModel est une implémentation de DataModel et est utilisée dans le managed bean MagasinBean pour contenir la liste des articles.

Le workflow suivant illustre le code mis en place dans les différentes couches de l’application pour produire l’affichage de la liste des articles :

Lorsque le client clique sur le lien Liste des articles dans le menu de gauche, la page listeArticles.xhtml est affichée :

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 266: Ejb Jsf Struts Flex Jasper

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/template.xhtml"> <ui:define name="titre"> <h:outputText value="#lesMessages.listeArticles_titre"/> </ui:define> <ui:define name="contenu"> <center> <f:view > <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form> <div align="right" > <h:graphicImage url="/images/utilisateur.gif" /><br /> <h:outputText value="#lesMessages.general_nom" /> : <h:outputText value="#sessionScope.loginBean.client.nom"/><br/> <h:outputText value="#lesMessages.general_prenom" />: <h:outputText value="#sessionScope.loginBean.client.prenom"/> </div> <h2><h:outputFormat value="#lesMessages.listeArticles_voici"> <f:param value="#magasinBean.pagination.nombreArticles"/> </h:outputFormat> </h2> <h3><h:outputText value="Articles #magasinBean.pagination.premierArticle + 1 à #magasinBean.pagination.dernierArticle"/></h3> <br /> <br /> <h:commandButton action="#magasinBean.afficherPrecedentsArticles" value="Précédent" rendered="#magasinBean.pagination.existeArticlesPrecedents" /> &nbsp; &nbsp; &nbsp; <h:commandButton action="#magasinBean.afficherProchainsArticles" value="Suivant" rendered="#magasinBean.pagination.existeArticlesSuivants"/> <br /> <br /> <h:dataTable value="#magasinBean.listeArticlesMagasinModel" rendered="#not empty magasinBean.listeArticlesMagasinModel " var="item" bgcolor="#F1F1F1" border="10" cellpadding="5" cellspacing="3" first="0" width="50%" dir="LTR" frame="hsides" rules="all" summary="Ceci est la dataTable pour afficher la liste des articles" rowClasses="TableLigne1,Tableligne2" columnClasses="TableColonne" styleClass="TableClass" headerClass="TableHeader"> <h:column> <f:facet name="header" > <h:outputText value="Nom catalogue"/> </f:facet> <h:outputText value="#item.nomCatalogue"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="Nom produit"/> </f:facet> <h:outputText value="#item.nomProduit"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="Nom article"/> </f:facet> <h:outputText value="#item.nomArticle"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="ID"/>

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 267: Ejb Jsf Struts Flex Jasper

</f:facet> <h:outputText value="#item.id"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="Prix"/> </f:facet> <h:outputText value="#item.prix"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="Quantite en stock"/> </f:facet> <h:outputText value="#item.quantite"/> </h:column> <h:column> <h:link value="Visualiser" outcome="afficherDetails" > <f:param name="id" value="#item.id"/> <h:graphicImage url="../images/loupe.png" /> </h:link> </h:column> </h:dataTable> </h:form> </f:view> </center> </ui:define> </ui:composition>

Celle­ci possède un tag <h:dataTable> qui génère une table HTML alimentée par la liste articles qui est alimentée dans le bean MagasinBean. Le tag <h:dataTable> peut être customisé avec des styles CSS définis pour les colonnes (attribut columnClasses), pour le header (attribut headerClass), les éléments de la table (attribut styleClass), les lignes paires et impaires (rowClasses).

Ce fichier XHTML fait usage du nouveau tag <h:link>. Ce tag a l’avantage de rendre les URLS bookmarkables. C’est le cas aussi d’un autre nouveau tag : <h:button>. Concrètement, l’ID de l’article dont on souhaite afficher les détails apparait dans le lien :

http://localhost:8085/VenteEnLigneWebJSF2/magasin/afficherDetails.jsf?id=1

C’est un avantage par rapport au tag <h:commandLink> par exemple qui masque cet ID dans l’URL. Voici le code du lien si le tag <h:commandLink> est utilisé à la place du tag <h:link> :

<h:commandLink value="Visualiser" action="#magasinBean.afficherDetails" > <f:param name="id" value="#item.id"/> <h:graphicImage url="../images/loupe.png" /> </h:commandLink>

Et voici l’URL du lien génére :

http://localhost:8085/VenteEnLigneWebJSF2/magasin/afficherDetails.jsf

Cette URL ne possède pas d’ID d’article, elle n’est donc pas bookmarkable.

Le tag <h:link> ne possède pas d’attribut action, contrairement au tag <h:commandLink>. Une solution pour appeler la méthode afficherDetails est alors de la spécifier dans l’attribut beforePhase du tag <f:view> de la page magasin/afficherDetails.xhtml. Le paramètre id de la requête est transmis dans l’attribut detailsArticleId du managed bean MagasinBean grâce à deux autres nouveaux attributs : <f:metadata> et <f:viewParam>. Ainsi, dans le fichier afficherDetails.xhtml (détaillé dans le paragraphe Intégration d’AJAX), la vue est enrichie de meta données :

<f:metadata> <f:viewParam name="id" value="#magasinBean.detailsArticleId" /> </f:metadata> <f:view beforePhase="#magasinBean.afficherDetails" > ... </f:view>

Ce paramètre est nécessaire dans la méthode afficherDetails() qui récupère les détails de l’article. À l’intérieur du tag

Bookmark

- 19 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 268: Ejb Jsf Struts Flex Jasper

<f:metadata>, le tag <f:viewParam> stocke un paramètre de la vue dans une propriété. Concrètement, il permet de setter la valeur de la propriété detailsArticleId du managed bean magasinBean à la valeur du paramètre id de la vue et qui fait parti de la requête. Il mappe donc des paramètres de la requête avec des paramètres de la vue. Cette propriété est ensuite utilisée pour rechercher les détails de l’article.

Ainsi, quand le framework JSF traite la requête de type GET afficherDetails.jsf?id=1, la valeur de la propriété #magasinBean.detailsArticleId est de 1.

Dans la page listeArticles.xhtml, l’attribut includeViewParams est setté à true. Cela signifie que les paramètres de la vue actuelle doivent être inclus dans la vue suivante. Le tag <f:metadata> définit donc une section en haut de la vue et qui contient un ou plusieurs tags <f :viewParam>. Le schéma suivant illustre les interactions entre les pages avec les paramètres :

La pagination est souvent souhaitable dans une application, soit parce que l’écran est trop petit pour pouvoir afficher toutes les données, soit parce qu’afficher toutes les données dans une seule page risque de saturer la mémoire ou de prendre trop de temps si les données sont volumineuses.

Côté couche présentation, la pagination est réalisée à l’aide des boutons Précédent et Suivant, rendus par des tags <h:commandButton>. Côté couche métier, la pagination est gérée avec une classe utilitaire Pagination.java dont voici le code :

package com.eni.dvtejb.clientJSF2.services; import java.io.Serializable; import org.jboss.logging.Logger; public class Pagination implements Serializable private int nombreArticles = 0; private int nombreLignes = 6;

Pagination

- 20 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 269: Ejb Jsf Struts Flex Jasper

private int premierArticle = 0; public int getNombreLignes() return nombreLignes; public int getNombreArticles() return nombreArticles; public void setNombreArticles(int nombreArticles) this.nombreArticles = nombreArticles; public int getPremierArticle() return premierArticle; public void setPremierArticle(int premierArticle) this.premierArticle = premierArticle; public int getDernierArticle() int dernierArticle = 0; if ((premierArticle + nombreLignes) > nombreArticles ) dernierArticle = nombreArticles; else dernierArticle = premierArticle + nombreLignes; return dernierArticle; public void pageSuivante() if ((premierArticle + nombreLignes) < nombreArticles) premierArticle = premierArticle + nombreLignes; public void pagePrecedente() premierArticle = premierArticle - nombreLignes; if (premierArticle < 0) premierArticle = 0; public boolean getExisteArticlesSuivants() boolean existe = false; if (getDernierArticle() < getNombreArticles()) existe = true; return existe; public boolean getExisteArticlesPrecedents() boolean existe = false; if (premierArticle >= nombreLignes) existe = true; return existe;

L’affichage des boutons Précédent et Suivant est conditionné par la présence ou non d’articles dont l’existence est vérifiée avec les méthodes getExisteArticlesSuivants() et getExisteArticlesPrecedents().

- 21 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 270: Ejb Jsf Struts Flex Jasper

Dans le managed bean MagasinBean, la méthode getArticlesIntervalles() renvoie à chaque appel (boutons Suivant et Précédent) une liste de six articles qui sont compris entre l’intervalle [1er article, 1er article + (nombreLignes)].

@ManagedBean @SessionScoped public class MagasinBean public MagasinBean() pagination = new Pagination(); private Pagination pagination = null; private Article article; List<MagasinDTO> articlesIntervalles; public Pagination getPagination() pagination.setNombreArticles(getNombreTotalArticles()); return pagination; public int getNombreTotalArticles() return facade.getNombreTotalArticles(); public Article getArticle() return article; public void setArticle(Article article) this.article = article; DataModel<MagasinDTO> listeArticlesMagasinModel = new ListDataModel<MagasinDTO>(); public DataModel<MagasinDTO> getListeArticlesMagasinModel() listeArticlesMagasinModel.setWrappedData(getArticlesIntervalles()); return listeArticlesMagasinModel;

- 22 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 271: Ejb Jsf Struts Flex Jasper

public void setListeArticlesMagasinModel(DataModel<MagasinDTO> listeArticlesMagasinModel ) this.listeArticlesMagasinModel = listeArticlesMagasinModel; @EJB(name="VenteEnLigne/FacadeBean/remote") private FacadeRemote facade; public List<MagasinDTO> getArticlesIntervalles() if (articlesIntervalles == null) getPagination(); articlesIntervalles = getArticlesSuivants(pagination.getNombreLignes(), pagination.getPremierArticle()); return articlesIntervalles; public List<MagasinDTO> getArticlesSuivants(int max, int premier) return facade.findBoutiqueIntervalle(max, premier); public String afficherProchainsArticles() article = null; articlesIntervalles = null; getPagination().pageSuivante(); return "listeArticles"; public String afficherPrecedentsArticles() article = null; articlesIntervalles = null; getPagination().pagePrecedente(); return "listeArticles";

La pagination doit également être gérée au moment de la récupération des données en base. À chaque appel, il ne faut récupérer que les valeurs qui doivent être affichées. Ceci est implémenté dans le stateful session bean FacadeBean avec la méthode findBoutiqueIntervalle(int max, int premier) qui récupère, parmi les lignes retournées, un intervalle de noms d’articles avec leur catalogue, leur produit, l’ID, le prix et la quantité disponible en stock. Cet intervalle est compris entre la ligne d’indice premier et la ligne d’indice premier + max. Les méthodes setMaxResults() et setFirstResult() de la classe javax.persistence.Query spécifient l’intervalle de la requête en limitant le nombre d’objets retournés de la base de données :

public List<MagasinDTO> findBoutiqueIntervalle (int max, int premier) String requete = "SELECT NEW com.eni.dvtejb.metier.dtos.MagasinDTO( c.nom, p.nom, a.nom, a.articleid, a.prix, s.quantite) " + " FROM Catalogue c, Produit p, Article a, Stock s" + " WHERE c.catalogueid = p.catalogueFk" + " AND p.produitid = a.produitFk" + " AND a.stockFK = s.stockid"; Query req= entityManager.createQuery(requete); req.setMaxResults(max); req.setFirstResult(premier); return req.getResultList();

Pour une meilleure lisibilité du code, il est recommandé de mettre les mots clés du langage JPQL en majuscules afin de les distinguer des noms des entity beans et des variables.

Cette requête utilise la clause « SELECT NEW », qui est une clause avancée du langage JPQL, pour charger les données retournées par la requête dans un objet de transfert, MagasinDTO, selon le design pattern DTO (Data Transfer Object). Elle renvoie donc une liste d’instances de la classe MagasinDTO. Cette classe doit être renseignée avec le package dont elle fait partie. Elle possède un constructeur qui prend en paramètres l’ensemble des données retournées par la requête :

- 23 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 272: Ejb Jsf Struts Flex Jasper

public MagasinDTO(String nomCatalogue, String nomProduit, String nomArticle, long id, double prix, BigDecimal quantite) super(); this.nomCatalogue = nomCatalogue; this.nomProduit = nomProduit; this.nomArticle = nomArticle; this.id = id; this.prix = prix; this.quantite = quantite;

6. Affichage de l’historique des commandes

L’affichage de l’historique des commandes repose également sur un composant dataTable. Le workflow est similaire à celui de l’affichage de la liste des articles.

Une alternative à l’initialisation de la liste dans la méthode getter de cette liste peut être l’appel d’une méthode avant la phase d’application des paramètres de la requête. Pour cela, il existe l’attribut beforePhase du tag <f:view> qui spécifie le nom de la méthode à appeler. Cette méthode est appelée avant un changement de phase. C’est le cas de l’affichage de l’historique des commandes.

Facelet historiqueCommandes.xhtml :

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//

- 24 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 273: Ejb Jsf Struts Flex Jasper

EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/template.xhtml"> <ui:define name="titre"> <h:outputText value="#lesMessages.listeArticles_titre"/> </ui:define> <ui:define name="contenu"> <center> <f:view beforePhase="#magasinBean.afficherCommandesPrecedentes" > <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form> <div align="right" > <h:graphicImage url="/images/utilisateur.gif" /><br /> <h:outputText value="#lesMessages.general_nom" /> : <h:outputText value="#sessionScope.loginBean.client.nom"/><br/> <h:outputText value="#lesMessages.general_prenom" />: <h:outputText value="#sessionScope.loginBean.client.prenom"/> </div> <h2><h:outputText value="Voici l’historique de vos commandes" /></h2> <h:dataTable value="#magasinBean.commandes" rendered="#not empty magasinBean.commandes" var="CommandeDTO" bgcolor="#F1F1F1" border="10" cellpadding="5" cellspacing="3" first="0" width="50%" dir="LTR" frame="hsides" rules="all" summary="Ceci est la dataTable pour afficher lhistorique des commandes." rowClasses="Tableligne1,TableLigne2" columnClasses="TableColonne" styleClass="TableClass" headerClass="TableHeader"> <h:column> <f:facet name="header" > <h:outputText value="ID Commande"/> </f:facet> <h:outputText value="#CommandeDTO.commandeId"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="Nom article"/> </f:facet> <h:outputText value="#CommandeDTO.nomArticle"/> </h:column> <h:column> <f:facet name="header" > <h:outputText value="Date commande" /> </f:facet> <h:outputText value="#CommandeDTO.dateCommande"> <f:convertDateTime pattern="dd.MM.yyyy" /> </h:outputText> </h:column> </h:dataTable> </h:form> </f:view> </center> </ui:define> </ui:composition>

Le convertisseur <f:convertDateTime> convertit la date de la commande dans un format conforme au pattern dd.MM.yyyy. Le contenu de la dataTable qui liste l’historique des commandes est chargé avant la phase d’application des paramètres de la requête. Dans le managed bean MagasinBean, la méthode afficherCommandesPrecedentes(PhaseEvent event) d’initialisation de la liste prend en paramètre un objet javax.faces.event.PhaseEvent pour être à l’écoute d’un changement de phase.

- 25 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 274: Ejb Jsf Struts Flex Jasper

Extrait de MagasinBean.java :

public String afficherCommandesPrecedentes(PhaseEvent event) FacesContext context = FacesContext.getCurrentInstance(); HttpSession session = (HttpSession) context.getExternalContext().getSession(true); LoginBean lb = null; lb = (LoginBean) session.getAttribute("loginBean"); Client cl = (Client) lb.getClient(); Utilisateur u = (Utilisateur)cl; commandes = panierBean.afficherCommandesPrecedentes(u); return "historiqueCommandes";

7. Injection de dépendance et Weld

Nous avons vu précédemment qu’il est possible d’injecter des EJB de type session beans dans les managed beans avec l’annotation @EJB. JSF agit en effet comme un container qui supporte l’injection de dépendance. L’annotation @javax.faces.bean.ManagedProperty rend également possible l’injection d’objets dans des champs de managed beans. Cette annotation remplace le tag <managed­property> dans le fichier faces­config.xml. L’injection s’appuie sur la méthode setter de l’objet à injecter.

Pour afficher les détails d’un article, la création d’une méthode de recherche d’un article suivant son Id est nécessaire. Certaines méthodes de recherche sont souvent demandées dans les algorithmes, il est alors souhaitable de les externaliser dans une classe managée de services pour pouvoir les réutiliser. L’idée est donc d’injecter cette classe de services dans les managed beans qui souhaitent utiliser des méthodes de cette classe.

La classe Services est un managed bean car elle est annotée avec l’annotation @ManagedBean. L’attribut name assigne le nom ServicesVenteEnLigne au managed bean.

package com.eni.dvtejb.clientJSF2.services; import javax.ejb.EJB; import javax.faces.bean.ApplicationScoped; import javax.faces.bean.ManagedBean; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.sessions.PanierBeanRemote; @ManagedBean(name="ServicesVenteEnLigne") @ApplicationScoped public class Services private static final Logger log = Logger.getLogger(Services.class); @EJB(name="VenteEnLigne/PanierBean/remote")

- 26 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 275: Ejb Jsf Struts Flex Jasper

private PanierBeanRemote panierBean; public Article rechercheDetails(String numeroId) long lID = Long.parseLong(numeroId); Article monArticle = panierBean.findById(lID); return monArticle; ...

Dans le managed bean MagasinBean, l’injection du managed bean Services est faite avec l’annotation @ManagedProperty. L’objet sera injecté dans le setter. Le nom de l’objet injecté est référencé par son nom, en faisant usage d’une expression EL : #ServicesVenteEnLigne . Voici la partie du code dans le managed bean MagasinBean qui inclut l’utilisation de cette annotation :

@ManagedBean @SessionScoped public class MagasinBean ... // Injection d’une instance de la classe Services @ManagedProperty(value = "#ServicesVenteEnLigne") private Services services; // Setter obligatoire public void setServices(Services services) this.services = services; ... public String afficherDetails(PhaseEvent event) FacesContext context = FacesContext.getCurrentInstance(); Map<String, String> params = context.getExternalContext().getRequestParameterMap(); String numeroId = params.get("id"); Article monArticle = services.rechercheDetails(numeroId); if (null != monArticle) String nomArticle = monArticle.getNom(); log.info("Le nom de l’article est :" + nomArticle); article = monArticle; else log.info("ARTICLE NUL"); return "/magasin/afficherDetails";

La méthode afficherDetails(PhaseEvent event) est appelée dans l’attribut beforePhase du tag <f:view> de la page afficherDetails.xhtml. L’affichage du détail d’un article est décrit dans le paragraphe suivant.

Un paragraphe sur l’injection de dépendance dans les managed beans serait incomplet sans évoquer les nouveaux apports de Java EE 6 dans ce domaine. En effet, Java EE 6 apporte des spécifications qui tendent à standardiser l’injection de dépendance et la création de managed beans. C’est le cas des nouvelles JSR 330 (Injection de dépendance pour Java) et JSR 299 (Context and Dependency Injection, anciennement appelée WebBeans) ainsi que de la mise à jour de la JSR 250 (Managed Beans).

JBoss 6 intègre Weld, l’implémentation de référence de CDI. Ce paragraphe fournit donc une brève introduction à Weld.

Weld apporte une alternative intéressante à la création de managed beans dans JSF. Au lieu d’utiliser l’annotation @ManagedBean sur la classe, on peut utiliser l’annotation @javax.inject.Named. La librairie Weld est weld­core.jar. Elle se trouve dans le répertoire jboss­6.0.0.M1\server\default\deployers\weld.deployer\lib­int\. Dans un projet de type Web, Weld requiert la présence d’un fichier vide beans.xml qui peut être créé dans le répertoire /WEB­INF. Ce fichier sert à signaler au container la présence de beans et à mettre en place les services CDI pour l’application.

On peut considérer une page dans l’application où l’avis du client sur le site est solicité par une question générée de façon aléatoire, parmi une liste de questions.

Le code du bean QuestionnaireBean est le suivant :

Weld

- 27 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 276: Ejb Jsf Struts Flex Jasper

package com.eni.dvtejb.clientJSF2.weld; import javax.annotation.PostConstruct; import javax.enterprise.inject.spi.BeanManager; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.inject.Inject; import javax.inject.Named; import org.apache.log4j.Logger; @Named("questionnaireVenteEnLigne") public class QuestionnaireBean private static final Logger log = Logger.getLogger(QuestionnaireBean.class); @Inject @QuestionHasard private Question question; private String reponse; public String getReponse() return reponse; public void setReponse(String reponse) this.reponse = reponse; @PostConstruct public void init () log.info(QuestionnaireBean.class.getSimpleName() + " a été instancié."); public String getQuestionAleatoire() return question.laQuestion; // sauver la réponse en base public void sauverReponse() // ... addMessage("Merci, votre réponse est prise en compte."); public static void addMessage(String msg) FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg); FacesContext fc = FacesContext.getCurrentInstance(); fc.addMessage(null, facesMsg);

Ce bean est annoté avec l’annotation @javax.inject.Named("questionnaireVenteEnLigne"). Il sera donc accessible à partir de la facelets questionnaire.xhtml avec le nom questionnaireVenteEnLigne. L’annotation @javax.inject.Inject indique que le champ question doit être injecté. C’est le point d’injection. La question aléatoire est ainsi injectée dans le bean. Dans cet exemple, elle est suivie de l’annotation @QuestionHasard. C’est une annotation créée à l’aide de l’annotation @javax.inject.Qualifier.

Annotation QuestionHasard.java :

package com.eni.dvtejb.clientJSF2.weld; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME;

- 28 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 277: Ejb Jsf Struts Flex Jasper

import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; @Target( TYPE, METHOD, PARAMETER, FIELD ) @Retention(RUNTIME) @Documented @Qualifier public @interface QuestionHasard

L’annotation @QuestionHasard est également utilisée après l’annotation @javax.enterprise.inject.Produces dans la méthode questionHasard() de la classe Questions.java. L’annotation @Produces est là pour indiquer que le résultat de la méthode est disponible pour être injecté. Ce résultat peut être appliqué à un qualifier, en faisant suivre l’annotation @Produces par une autre annotation, comme c’est le cas de l’annotation @QuestionHasard.

Voici la classe Questions.java qui contient la méthode questionHasard() qui génère une question aléatoire :

package com.eni.dvtejb.clientJSF2.weld; import java.util.ArrayList; import java.util.List; import javax.enterprise.inject.Produces; public class Questions List<String> questions; public Questions() questions = new ArrayList<String>(); questions.add(0, "Etes-vous satisfait la liste des articles proposés ?"); questions.add(1, "Allez-vous commander sur notre site dans les prochains jours ?"); questions.add(2, "Quel(s) autre(s) catalogue(s) souhaitez-vous voir sur le site ?"); questions.add(3, "Etes-vous satisfait de l’ergonomie du site ?"); @Produces @QuestionHasard public Question questionHasard() Question q = new Question(); int nb; nb = (int)(Math.random()*questions.size()); q.laQuestion = questions.get(nb); return q;

Les questions ont été insérées dans le code mais, dans un scénario de production, les questions sont insérées via un CMS (Content Management System) ou directement dans une table en base pour récupération par la suite. Enfin, voici un extrait de la facelet questionnaire.xhtml qui utilise le bean injecté :

<f:view > <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form> <body> <center> <h2>Question aléatoire</h2> <h4>Aidez-nous à connaître vos attentes en répondant à la question aléatoire suivante :</h4> <h:panelGrid columns="2" cellpadding="3px">

- 29 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 278: Ejb Jsf Struts Flex Jasper

<h:outputLabel for="laQuestion" value="Question :"/> <h:outputText type="text" id="laQuestion" value="#questionnaireVenteEnLigne.questionAleatoire" /> <h:outputLabel for="laReponse" value="Réponse :"/> <h:inputText id="laReponse" size="100" value="#questionnaireVenteEnLigne.reponse"/> </h:panelGrid> <h:commandButton value="Soumettre" action="#questionnaireVenteEnLigne.sauverReponse" /> </center> </body> </h:form> <h4> <h:messages /></h4> </f:view>

8. Création du composant composite detailsArticle

Dans la version 2.0, Facelets ne sert pas qu’à créer des templates. Il sert également à créer des composants graphiques personnalisés. Un composant est un élément qui est réutilisable et configurable. La possibilité de créer ses propres composants dans JSF n’est pas nouvelle. Mais dans JSF 2.0, la création de composants simples est facilitée grâce aux Facelets, la librairie Composite et à un nouveau langage déclaratif, le PDL (Page Declaration Language). Il n’est plus nécessaire d’hériter la classe javax.faces.component.UIComponent, de créer un fichier TLD (Tag Library Descriptor) et de déclarer le composant dans le fichier faces­config.xml. La création d’un composant se fait de manière déclarative dans un unique fichier XHTML. Cependant pour des composants plus élaborés, il faudra recourir à des classes Java.

Les étapes pour la création d’un composant sont les suivantes :

1­Créer le fichier <nomComposant>.xhtml du composant dans le répertoire webapp/resources/ <nomRepertoireComposants>. Créer d’abord le répertoire resources s’il n’existe pas.

2­Déclarer la partie interface et la partie implémentation du composant.

3­Inclure le composant dans le fichier XHTML appelant.

Un exemple de composant dans l’application peut être la table qui affiche les détails d’un article. Cette table peut être englobée à l’intérieur d’un composant pour être réutilisée dans d’autres parties de l’application. Le nom d’un composant est celui du fichier XHTML.

Voici le code du composant detailsArticle définit dans le fichier webapp/resources/ezcomp/detailsArticle.xhtml :

<?xml version="1.0" encoding="ISO-8859-1"?> <ui:component xmlns="http://www.w3.org/1999/xhtml" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:composite="http://java.sun.com/jsf/composite" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd"> <h:head> <link href="../css/styles.css" rel="stylesheet" type="text/css"/> <title>Details d’un article (Ce titre n’est pas affiche)</title> </h:head> <composite:interface> <composite:attribute name="IdArt" required="true" /> <composite:attribute name="NomArt" required="true"/> <composite:attribute name="PrixArt" required="true"/> <composite:attribute name="ImageArt" required="true"/> </composite:interface> <composite:implementation> <table class="table1" > <tr align="center"><td height="35">Id de l’article :</td><td>

- 30 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 279: Ejb Jsf Struts Flex Jasper

<h:outputText value="#cc.attrs.IdArt" /></td> </tr> <tr align="center"><td height="35">Nom de l’article :</td><td> <h:outputText value="#cc.attrs.NomArt" /> </td> </tr> <tr align="center"><td height="35">Prix de l’article :</td><td> <h:outputText value="#cc.attrs.PrixArt" /> </td> </tr> <tr align="center"><td >Image :</td><td> <h:graphicImage url="#cc.attrs.ImageArt"/> </td> </tr> </table> </composite:implementation> </ui:component>

Ce composant est encapsulé dans un tag <ui:component> et est construit à partir de deux tags qui délimitent deux sections : l’interface et l’implémentation. Le premier tag, <composite:interface>, déclare le contrat d’un composant composite. Dans ce contrat, on trouve les attributs, obligatoires ou non, du composant et qui sont déclarés avec le tag <composite:attribute>.

Le deuxième tag, <composite:implementation>, représente le layout du composant.

L’expression #cc.attrs.ATTRIBUTE_NAME donne accès aux propriétés définies dans la section interface. cc (composite component) est un mot­réservé dans JSF 2.

Au niveau de l’utilisation de ce composant dans le fichier XHTML appelant, il faut déclarer l’espace de nom de la taglib qui contient le composant composite. L’URI de cet espace de nom doit être du style http://java.sun.com/jsf/composite/<nomRepertoireComposants> où <nomRepertoireComposants> est le répertoire où sont stockés les composants.

La capture d’écran suivante montre l’emplacement du composant dans l’exporateur du projet sous Eclipse :

- 31 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 280: Ejb Jsf Struts Flex Jasper

C’est une ressource de librairies. Voici donc le code du fichier afficherDetails.xhtml qui utilise le composant detailsArticle :

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/template.xhtml" xmlns:ez="http://java.sun.com/jsf/composite/ezcomp"> <ui:define name="titre"> <h:outputText value="Details de l’article"/> </ui:define> <ui:define name="contenu"> <center> <f:view > <f:metadata> <f:viewParam name="id" value="#magasinBean.detailsArticleId" /> </f:metadata> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages"

- 32 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 281: Ejb Jsf Struts Flex Jasper

var="lesMessages"/> <h:form> <br /><br /> <h1>Détails</h1> <ez:detailsArticle IdArt="#magasinBean.article.articleid" NomArt="#magasinBean.article.nom" PrixArt="#magasinBean.article.prix" ImageArt="/AfficheImageServlet?numeroId=#magasinBean.article.articleid"/> </h:form> </f:view> </center> </ui:define> </ui:composition>

L’insertion du tag <ez:detailsArticle > avec les attributs requis suffit à insérer le composant personnalisé qui affiche les détails d’un article.

9. Intégration d’AJAX

Depuis la version 2.0, grâce au nouveau tag <f:ajax>, il n’est pas nécessaire d’insérer du code JavaScript dans une page ou de faire appel à des librairies tierces (telles que MyFaces Tomahawk, JBoss Ajax4jsf/RichFaces, PrimeFaces, Icesoft ICEfaces, DynaFaces, etc.) pour « ajaxiser » une application, comme par exemple faire des appels asynchrones et mettre à jour des parties d’une page sans avoir à recharger la page dans son intégralité, contrairement aux versions précédentes.

Vous pouvez trouver l’article en français de Jesse James Garrett, qui a lancé le terme Ajax, à l’URL suivante : http://www.scriptol.fr/ajax/ajax­garrett.php

Au niveau de la page d’accueil d’un site, il est courant d’insérer un lien qui permet à un utilisateur de retrouver son mot de passe en cas d’oubli. La page de récupération du mot de passe suivante demande à l’utilisateur de saisir son login et son mot de passe et utilise Ajax pour communiquer avec le serveur de sorte que le mot de passe est affiché sur la même page, en modifiant le DOM, et sans rafraîchissement. Le DOM (Document Object Model) représente la page Web qui est actuellement chargée dans le navigateur. On peut accéder et modifier les éléments et attributs d’un DOM avec des méthodes et des attributs qui commencent par « document ».

Page oubliMDP.xhtml :

<?xml version="1.0" encoding="ISO-8859-1"?>

- 33 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 282: Ejb Jsf Struts Flex Jasper

<!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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui=" http://java.sun.com/jsf/Facelets"> <h:head> <title><h:outputText value="Oubli du mot de passe"/></title> <link rel="stylesheet" type="text/css" href="css/styles.css" /> </h:head> <f:view locale="#loginBean.locale"> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form id="Form1"> <h:body> <center> <br /><br /><br /><br /> <h:panelGrid columns="2" id="panel" border="1" bgcolor="#CECEFF"> <f:facet name="header"> <h:outputText value="Oubli du mot de passe"/> </f:facet> <h:outputLabel for="login" value="#lesMessages.login_label1" /> <h:inputText id="login" value="#loginBean.login" required="true" /> <h:outputLabel for="password" value="Entrez votre email" /> <h:inputText id="email" value="#loginBean.email" required="true" /> <f:facet name="footer"> <h:panelGroup style="display:block; text-align:center"> <h:commandButton id="submit" value="Valider" action="#loginBean.oubliMDP" > <f:ajax execute="login email" render="Form1:champMDP"/> </h:commandButton> </h:panelGroup> </f:facet> </h:panelGrid> <br /> <h2><h:outputText value="#loginBean.password" id="champMDP" /></h2> <h:messages /> </center> </h:body> </h:form> </f:view> </html>

Il est possible d’utiliser la ligne de chargement des messages du bundle directement dans le fichier du template pour ne pas avoir à le faire dans chaque page XHTML.

Le schéma suivant illustre le fonctionnement de l’appel du tag <f:ajax> dans la fonctionnalité de récupération du mot de passe :

- 34 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 283: Ejb Jsf Struts Flex Jasper

Une méthode oubliMDP() est ajoutée dans le managed bean LoginBean. Son rôle est d’appeller la méthode findMdpByLoginEmail(String login, String email) du stateful session bean FacadeBean qui retourne le mot de passe. La variable password du managed bean est ensuite mise à jour avec ce mot de passe.

L’attribut render du tag <f:ajax> sert à spécifier une liste d’éléments, séparés par des espaces, de la page à mettre à jour. Cet élément est identifié par son Id. L’attribut execute spécifie une liste d’éléments, séparés par des espaces, de la page à traiter par le serveur. Si la valeur « @form » est affectée à cet attribut, alors tous les éléments du formulaire parent seront traités par le serveur. Les valeurs prédéfinies et communes aux attributs render et execute sont les suivantes :

@this : le composant qui déclenche la requête Ajax ;

@form : tous les composants du formulaire qui contient le composant à l’intérieur duquel se trouve le tag <f:ajax> ;

@all : tous les composants de la vue ;

@none : aucun composant.

L’attribut onevent spécifie une fonction JavaScript à exécuter quand un évènement est déclenché. Enfin, l’attribut event fournit la possibilité de choisir l’évènement déclenché qui va exécuter le tag.

Au niveau des IDs à spécifier dans les tags render et execute, il faut savoir que dans le tag <form>, l’attribut prependId, lorsqu’il est setté à false, ne génère pas, au niveau du code HTML résultant, de concaténation de l’ID d’un composant avec l’ID de la forme à laquelle il appartient. Par exemple le code suivant :

<h:form id="Form1" > ... <h:inputText id="login" value="#loginBean.login" required="true" /> ... </h:form>

génère, par défaut, le code HTML suivant :

<input id="Form1:login" type="text" name="login" />

En revanche, l’ajout de l’attribut prependId avec la valeur false au composant form :

<h:form id="Form1" prependId="false"> ... <h:inputText id="login" value="#loginBean.login" required="true" /> ... </h:form>

Les attributs du tag <f:ajax>

- 35 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 284: Ejb Jsf Struts Flex Jasper

ne génère pas de concaténation des IDs dans le code HTML :

<input id="login" type="text" name="login" />

Ceci est à prendre en compte dans la mesure où Ajax recherche les IDs des composants lors du traitement d’une requête.

Voici la partie de code du managed bean LoginBean qui gère l’oubli d’un mot de passe :

@ManagedBean @SessionScoped public class LoginBean private String password; public String getPassword() return password; public void setPassword(String password) this.password = password; ... public void oubliMDP() String mdp = facade.findMdpByLoginEmail(login, email); password = "Votre mot de passe est " + mdp;

Le message avec le mot de passe s’affiche directement sous la table, sans que l’écran soit rafraîchi :

Une alternative au tag <f:ajax> est l’utilisation de l’API JavaScript jsf.ajax.request(). L’inclusion de cette API dans une page se fait avec le nouveau tag <h:outputScript> pour inclure le fichier jsf.js :

<h:outputScript name="jsf.js" library="javax.faces" target="head"/>

Le fichier JavaScript jsf.js se trouve à l’intérieur de la librairie jboss­6.0.0.M1\server\default\deploy\jbossweb.sar\jsf­libs\ jsf­impl.jar, dans le répertoire META­INF\resources\javax.faces.

Le site http://www.jsfmatrix.net/ liste les librairies qui proposent des composants JSF Ajax.

- 36 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 285: Ejb Jsf Struts Flex Jasper

10. Recherche d’articles

La recherche d’articles par catalogue ou par produit passe par le stateless session bean RechercheBean. La démarche est la même que pour la récupération de la liste des articles : des requêtes SELECT NEW sont exécutées et les résultats retournés sont encapsulés dans un objet DTO, à savoir MagasinDTO.

Voici le code des méthodes de recherche dans le stateless session bean RechercheBean.java :

@SuppressWarnings("unchecked") public List<MagasinDTO> rechercheArticleParProduit (String nomProduit) String requete = "SELECT NEW com.eni.dvtejb.metier.dtos.MagasinDTO( c.nom, p.nom, a.nom, a.articleid, a.prix, s.quantite) " + " FROM Catalogue c, Produit p, Article a, Stocks" + " WHERE c.catalogueid = p.catalogueFk" + " AND p.produitid = a.produitFk" + " AND a.stockFK = s.stockid " + " AND p.nom = :nomArt" ; Query req= entityManager.createQuery(requete); req.setParameter("nomArt", nomProduit); return req.getResultList(); public List<MagasinDTO> rechercheArticleParCatalogue(long idCatalogue) String requete = "SELECT NEW com.eni.dvtejb.metier.dtos.MagasinDTO( c.nom, p.nom, a.nom, a.articleid, a.prix, s.quantite) " + " FROM Catalogue c, Produit p, Article a, Stocks" + " WHERE c.catalogueid = p.catalogueFk" + " AND p.produitid = a.produitFk" + " AND a.stockFK = s.stockid " + " AND c.catalogueid = :idCat" ; Query req= entityManager.createQuery(requete); req.setParameter("idCat", idCatalogue); return req.getResultList();

L’annotation @SuppressWarnings("unchecked") sur les deux méthodes permet de passer outre un warning qui est levé parce que la méthode getResultList() retourne une version non générique de List, contrairement aux types de retour des deux méthodes.

Les menus permettant de sélectionner le catalogue ou le produit pour la recherche, correspondent à des composants <h:selectOneMenu> et <h:selectOneListbox>. L’alimentation de ces composants à partir des listes (de produits et d’articles) peut être faite de plusieurs façons :

À partir du getter de la liste, comme le cas de la liste des catalogues. La méthode de récupération de tous les noms de catalogue est en effet appellée directement dans les getters.

Dans l’attribut beforePhase du tag <f:view>, comme le cas de la liste des produits.

Voici le code du managed bean RechercheJSFBean.java :

package com.eni.dvtejb.clientJSF2.beans; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.event.PhaseEvent; import javax.faces.model.SelectItem; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.dtos.MagasinDTO;

- 37 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 286: Ejb Jsf Struts Flex Jasper

import com.eni.dvtejb.metier.entities.Catalogue; import com.eni.dvtejb.metier.entities.Produit; import com.eni.dvtejb.metier.sessions.RechercheRemote; @ManagedBean @SessionScoped public class RechercheJSFBean private static final Logger log = Logger.getLogger(RechercheJSFBean.class); public RechercheJSFBean() private List<MagasinDTO> listeResultatArticles; private List<String> nomsProduitsListe; private List cataloguesListe; private String produitCritere; private long catalogueCritere; @EJB(name="VenteEnLigne/RechercheBean/remote") private RechercheRemote rechercheBean; public List<MagasinDTO> getListeResultatArticles() return listeResultatArticles; public void setListeResultatArticles(List<MagasinDTO> listeResultatArticles) this.listeResultatArticles = listeResultatArticles; public long getCatalogueCritere() return catalogueCritere; public void setCatalogueCritere(long catalogueCritere) this.catalogueCritere = catalogueCritere; public String getProduitCritere() return produitCritere; public void setProduitCritere(String produitCritere) this.produitCritere = produitCritere; public List<String> getNomsProduitsListe() return nomsProduitsListe; public List getCataloguesListe() rechercheListeCatalogues(); return cataloguesListe; public void rechercheListeProduits(PhaseEvent event) List<Produit> listeProduits ; List<String> listeNomsProduits = new ArrayList<String>(); listeProduits = rechercheBean.rechercheListeProduits(); for (Produit produit : listeProduits) listeNomsProduits.add(produit.getNom()); nomsProduitsListe = listeNomsProduits; public void rechercheListeCatalogues() List<Catalogue> listeCatalogues ; List listeDesCatalogues = new LinkedList();

- 38 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 287: Ejb Jsf Struts Flex Jasper

listeCatalogues = rechercheBean.rechercheListeCatalogues(); for (Catalogue catalogue : listeCatalogues) listeDesCatalogues.add(new SelectItem(catalogue.getCatalogueid(), catalogue.getNom())); cataloguesListe = listeDesCatalogues; public String rechercheArticleParProduit() List<MagasinDTO> listeArticles ; listeArticles = rechercheBean.rechercheArticleParProduit(produitCritere); listeResultatArticles = listeArticles; return "listeResultatRecherche"; public String rechercheArticleParCatalogue() List<MagasinDTO> listeArticles ; listeArticles = rechercheBean.rechercheArticleParCatalogue(catalogueCritere); listeResultatArticles = listeArticles; return "listeResultatRecherche";

Côté interface, la facelet recherche.xhtml affiche la liste des produits dans un menu déroulant et la liste des articles dans un menu figé.

<ui:define name="titre"> <h:outputText value="#lesMessages.listeArticles_titre"/> </ui:define> <ui:define name="contenu"> <center> <f:view beforePhase="#rechercheJSFBean.rechercheListeProduits" locale="#loginBean.locale"> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form> <h2>Recherche d’articles</h2> <h4>Sélectionnez votre critère de recherche</h4> <h4/> <table cellspacing="2" cellpadding="3" border="1" width="30%" > <tr align="center"> <td><h:outputText value="Produit"/></td> <td> <h:selectOneListbox value="#rechercheJSFBean.produitCritere"> <f:selectItems value="#rechercheJSFBean.nomsProduitsListe"/> </h:selectOneListbox></td> <td> <h:commandButton value="Recherche par produit" action="#rechercheJSFBean.rechercheArticleParProduit"/></td> </tr> <tr align="center"> <td><h:outputLabel value="Catalogue"/></td> <td> <h:selectOneMenu value="#rechercheJSFBean.catalogueCritere"> <f:selectItems value="#rechercheJSFBean.cataloguesListe"/> </h:selectOneMenu> </td> <td> <h:commandButton value="Recherche par catalogue" action="#rechercheJSFBean.rechercheArticleParCatalogue"/></td> </tr> </table> </h:form>

- 39 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 288: Ejb Jsf Struts Flex Jasper

</f:view> </center> </ui:define> </ui:composition>

Voici un écran qui illustre le rendu visuel des composants :

11. Validation et conversion dans les formulaires de contact et d’inscription

Le développement de formulaires implique la validation des données saisies. Pour cela JSF 2 met à notre disposition des validateurs par défaut. La liste complète des messages par défaut se trouve dans la librairie jsf­api.jar, sous le package javax.faces. Le fichier s’appelle Messages.properties. Lorsque ces validateurs par défaut ne suffisent pas, le développement de validateur personnalisé est nécessaire.

JSF 2 inclut l’annotation @javax.faces.validator.FacesValidator pour développer des validateurs personnalisés. Il n’est plus nécessaire de déclarer le validateur dans le fichier faces­config.xml.

Pour développer une classe validateur, il suffit d’annoter la classe avec cette annotation et d’implémenter l’interface javax.faces.validator.Validator.

Un formulaire de contact contient typiquement deux champs : un textarea dans lequel le visiteur saisit sa question ou ses commentaires et un champ email dans lequel il saisit son adresse email pour être contacté par la suite. Pour s’assurer que le visiteur a entré une adresse email correcte, le développement d’un validateur est nécessaire. Dans cet exemple, le mail est directement envoyé mais un EJB session pourrait très bien se charger d’insérer également le contenu du mail en base, via un entity bean et une table dédiée.

La classe du validateur d’adresse email est donc la suivante :

package com.eni.dvtejb.clientJSF2.validators; import java.util.ResourceBundle;

- 40 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 289: Ejb Jsf Struts Flex Jasper

import java.util.regex.Matcher; import java.util.regex.Pattern; 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; import org.apache.log4j.Logger; @FacesValidator("verificationEmailClient") public class EmailValidator implements Validator private static final Logger log = Logger.getLogger(EmailValidator.class); // messageBundleName récupère la valeur de l’attribut <message- bundle> dans faces-config.xml private static String messageBundleName = FacesContext.getCurrentInstance().getApplication().getMessageBundle(); public FacesMessage getMessage(FacesContext facesContext, String message) log.info("Entree dans la methode getMessage"); log.info("messageBundleName vaut : " + messageBundleName); ResourceBundle rb = ResourceBundle.getBundle(messageBundleName, facesContext.getViewRoot().getLocale()); String msg = rb.getString(message); FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_FATAL, msg, msg); return facesMsg; public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) throws ValidatorException log.info("Entree dans la methode de validation"); String email = (String) value; //Le pattern de l’adresse email Pattern pattern = Pattern.compile(".+@.+\\.[a-z]+"); //Création d’un matcher à partir de l’email avec le pattern Matcher ma = pattern.matcher(email); //Est-ce que l’email matche le pattern ? boolean match = ma.matches(); if (!match) throw new ValidatorException(getMessage(facesContext, "validator_MauvaisEmail"));

La méthode validate() est la méthode qui est appelée pour valider l’adresse email. Le message d’erreur renvoyé est récupéré dans les fichiers properties des messages.

La méthode getBundle( String nom, Locale locale) de la classe abstraite java.util.ResourceBundle s’appuie sur la valeur de l’attribut <message­bundle> définit dans le fichier faces­config.xml pour chercher le message à partir de la clé, dans le fichier de properties de la langue en cours. L’annotation @FacesValidator("verificationEmailClient") permet d’attribuer un nom à la classe du validateur. Il est ensuite référencé dans l’attribut validatorId de la balise <f:validator>.

- 41 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 290: Ejb Jsf Struts Flex Jasper

Extrait du fichier faces­config.xml :

<message-bundle>com.eni.dvtejb.clientJSF2.messages</message-bundle>

Lorsque l’adresse email est incorrecte, le message d’erreur est affiché au bas de la page, là où est placée la balise <h:messages>. Le formulaire de contact, contact.xhtml, s’appuie sur le bean managé ContactBean.java :

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/template.xhtml"> <ui:define name="titre"> <h:outputText value="#lesMessages.listeArticles_titre"/> </ui:define> <ui:define name="contenu"> <center> <f:view locale="#loginBean.locale"> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <div align="right" > <h:graphicImage url="/images/utilisateur.gif" /><br /> <h:outputText value="#lesMessages.general_nom" /> : <h:outputText value="#sessionScope.loginBean.client.nom"/><br/> <h:outputText value="#lesMessages.general_prenom" />: <h:outputText value="#sessionScope.loginBean.client.prenom"/> </div> <h:outputText value="#lesMessages.contact_Titre" /> <h:form id="contactDetails"> <br /> <h:inputTextarea type="text" rows="10" cols="90" id="message" value="#contactBean.message" required="true">

<f:converter converterId="commentairesConverter" />

<f:validateLength minimum="20"/> </h:inputTextarea> <br /><br /> <h:outputLabel value="#lesMessages.contact_Email" for="mailClient" /> <h:inputText size="45" type="text" id="mailClient" value="#contactBean.mailClient" required="true">

<f:validator

validatorId="verificationEmailClient" />

</h:inputText> <br /><br /> <h:outputLabel value="#lesMessages.contact_mailCC" for="mailCC" /> <h:selectBooleanCheckbox id="mailCC" value="#contactBean.mailCC" disabled="false" /> <br /><br /><br /> <h:commandButton type="submit" id="submit" action="#contactBean.envoiMail" value="#lesMessages.contact_mailBouton"/> <br /> </h:form> <br /><br /> <h:outputText value="#lesMessages.contact_mailOK" rendered="#contactBean.mailEnvoye" /> <h:messages /> </f:view> </center> </ui:define>

- 42 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 291: Ejb Jsf Struts Flex Jasper

</ui:composition>

Ce formulaire de contact s’appuie sur le bean managé ContactBean.java dont voici le code :

package com.eni.dvtejb.clientJSF2.beans; import java.io.UnsupportedEncodingException; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.mail.MessagingException; import javax.naming.NamingException; import javax.validation.constraints.Pattern; import org.apache.log4j.Logger; import com.eni.dvtejb.clientJSF2.services.EnvoiMail; @ManagedBean @SessionScoped public class ContactBean private static final Logger log = Logger.getLogger(ContactBean.class); private String message; private boolean mailEnvoye; private boolean mailCC; public boolean isMailCC() return mailCC; public void setMailCC(boolean mailCC) this.mailCC = mailCC; public String mailClient; // L’adresse email du client public String getMailClient() return mailClient; public void setMailClient(String mailClient) this.mailClient = mailClient; public ContactBean() mailEnvoye = false; public boolean isMailEnvoye() return mailEnvoye; public void setMailEnvoye(boolean mailEnvoye) this.mailEnvoye = mailEnvoye; public String getMessage() return message; public void setMessage(String message) this.message = message; public void envoiMail() EnvoiMail envoyerMail = new EnvoiMail(); try

- 43 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 292: Ejb Jsf Struts Flex Jasper

envoyerMail.envoiMailMessage("Questions client", mailClient, message, mailCC); catch (UnsupportedEncodingException e) e.printStackTrace(); catch (MessagingException e) e.printStackTrace(); catch (NamingException e) e.printStackTrace(); this.mailEnvoye = true;

Le fichier contact.xhtml contient le convertisseur commentairesConverter pour le champ message. Les principales motivations pour écrire un convertisseur sont la conversion des données d’un composant dans un type qui n’est pas standard et la conversion des formats des données.

Un convertisseur peut par exemple remplacer tous les points rencontrés dans les commentaires par la séquence de caractères ‘.\n’ afin d’ajouter des sauts de ligne pour s’assurer que le mail envoyé soit plus lisible. Voici le code de ce convertisseur, dans la classe CommentairesConverter :

package com.eni.dvtejb.clientJSF2.converters; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.FacesConverter; import org.apache.log4j.Logger; @FacesConverter(value ="commentairesConverter") public class CommentairesConverter implements Converter private static final Logger log = Logger.getLogger(CommentairesConverter.class); @Override public Object getAsObject(FacesContext arg0, UIComponent arg1, String newValue) String commentairesConvertis = null; if (newValue == null) return newValue; commentairesConvertis = newValue; commentairesConvertis = commentairesConvertis.replace(".", ". \n "); return commentairesConvertis; @Override public String getAsString(FacesContext arg0, UIComponent arg1, Object newValue) return newValue.toString();

L’annotation @FacesConverter évite de déclarer le convertisseur dans le fichier faces­config.xml. La classe doit implémenter l’interface javax.faces.convert.Converter.

Les deux méthodes à implémenter sont getObject() et getAsString(). Il est important de savoir que ces méthodes sont appelées à différentes phases du cycle de vie :

La méthode getObject() est appelée lors de la phase d’application des paramètres de la requête.

La méthode getAsString() est appelée lors de la phase de restitution de la réponse.

Dès que l’utilisateur soumet les données, la méthode getObject() est appelée et les commentaires sont parsés. La réponse renvoie des commentaires parsés dans le mail et à l’écran.

- 44 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 293: Ejb Jsf Struts Flex Jasper

Le listener de phases montre les appels des méthodes lors du cycle de vie de la requête :

21:07:34,418 INFO [STDOUT] Nouvelle requête en cours 21:07:34,419 INFO [STDOUT] avant - RESTORE_VIEW 1 21:07:34,436 INFO [STDOUT] après - RESTORE_VIEW 1 21:07:34,436 INFO [STDOUT] avant - APPLY_REQUEST_VALUES 2 21:07:34,437 INFO [STDOUT] après - APPLY_REQUEST_VALUES 2 21:07:34,437 INFO [STDOUT] avant - PROCESS_VALIDATIONS 3 21:07:34,439 INFO [CommentairesConverter] Appel de la méthode getAsObject() 21:07:34,471 INFO [EmailValidator] Entree dans la methode de validation 21:07:34,473 INFO [STDOUT] après - PROCESS_VALIDATIONS 3 21:07:34,473 INFO [STDOUT] avant - UPDATE_MODEL_VALUES 4 21:07:34,478 INFO [STDOUT] après - UPDATE_MODEL_VALUES 4 21:07:34,478 INFO [STDOUT] avant - INVOKE_APPLICATION 5 21:07:34,483 INFO [STDOUT] DEBUG: JavaMail version 1.4.2 ... 21:07:44,929 INFO [EnvoiMail] Message envoyé 21:07:45,357 INFO [STDOUT] après - INVOKE_APPLICATION 5 21:07:45,358 INFO [STDOUT] avant - RENDER_RESPONSE 6 21:07:45,385 INFO [CommentairesConverter] Appel de la méthode getAsString() 21:07:45,392 INFO [STDOUT] après - RENDER_RESPONSE 6 21:07:45,392 INFO [STDOUT] Fin d’analyse de la requête.

Les écrans suivants montrent le message et l’email entrés par le client avant sa soumission, puis le message converti et l’erreur de validation après soumission du formulaire.

Formulaire avant soumission :

Formulaire après soumission :

- 45 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 294: Ejb Jsf Struts Flex Jasper

Jason Carreira, le principal développeur du framework XWork (qui est devenu WebWork), a initialisé les spécifications sur la validation des beans. Puis Emmanuel Bernard, de JBoss, a travaillé sur ces spécifications (JSR­303) qui font maintenant partie de Java EE 6. Ces spécifications permettent de placer la validation au niveau de la couche du modèle. Elles ont été appliquées dans JSF 2.0. Concrètement, la validation peut se faire dans les managed beans (POJO) en ajoutant des annotations au niveau des propriétés. Ces annotations résident dans le package javax.validation. Parmi celles­ci se trouvent les suivantes :

@javax.validation.constraints.Max : la propriété doit être un nombre inférieur ou égal à une valeur spécifiée.

@javax.validation.constraints.Min : la propriété doit être un nombre supérieur ou égal à une valeur spécifiée.

@javax.validation.constraints.DecimalMin : la propriété doit être un nombre supérieur ou égal à une valeur spécifiée. Cette annotation supporte le type BigDecimal.

@javax.validation.constraints.Size : la taille de la propriété doit être comprise dans un intervalle spécifié.

@javax.validation.constraints.NotNull : la propriété ne doit pas être nulle.

@javax.validation.constraints.Pattern : la propriété doit correspondre à l’expression régulière.

@javax.validation.constraints.Future : la propriété doit être une date dans le futur.

Certaines de ces annotations sont mises en place dans le managed bean ClientBean associé à la page d’inscription d’un nouvel utilisateur :

Voici le code du managed bean ClientBean.java (les getters et setters ne sont pas inclus pour plus de lisibilité) :

package com.eni.dvtejb.clientJSF2.beans; import java.math.BigDecimal; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size;

Validation au niveau des beans

- 46 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 295: Ejb Jsf Struts Flex Jasper

import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.sessions.ClientDAO; @ManagedBean(name="leClient") @ViewScoped public class ClientBean private static final Logger log = Logger.getLogger(ClientBean.class); @Pattern(regexp=".+@.+\\.[a-z]+", message="Le format de l’adresse email est incorrect.") private String email; @Size(min=10, message="Le numéro de fax doit contenir 10 chiffres.") private String fax; @Size(min=5, message="Le login doit contenir au moins 5 caractères.") private String login; @Size(min=2, message="Le nom doit contenir au moins 2 caractères.") private String nom; // Expression régulière pour un password fort @Pattern(regexp="(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]8,10)$", message="Le mot de passe doit être compris entre 8 et 10 caractères, contenir au moins un chiffre et au moins une lettre. " + "Il ne doit pas contenir de caractères spéciaux.") private String password; @Size(min=2, message="Le prénom doit contenir au moins 2 caractères.") private String prenom; @Size(min=10, message="Le numéro de telephone doit contenir 10 chiffres.") private String telephone; @Size(min=2, message="Le titre doit contenir au moins 2 caractères.") private String titre; @Size(min=4, max=5, message="Le code postal doit contenir entre 4 et 5 chiffres.") @Min(value=1000, message="Le code postal doit être un chiffre supérieur ou égal à 1000") private String codepostal; private String departement; private BigDecimal numero; @NotNull @Min(value=3, message="Le pays doit contenir au moins 3 caractères") private String pays; private String rue; private String ville; private String succes; private Client nouveauClient; private Adresse adresseClient; private boolean loginPwdOK = false; private boolean infosOK = false; private boolean adresseOK = false; @EJB(name="VenteEnLigne/ClientDAOImpl/remote") ClientDAO clientDAO; ... // getters et setters

- 47 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 296: Ejb Jsf Struts Flex Jasper

public void validerLoginPwd() log.info("Entree dans la methode validerLoginPwd()"); nouveauClient = new Client(); nouveauClient.setLogin(login); nouveauClient.setPassword(password); loginPwdOK = true; return; public void validerInfos() log.info("Entree dans la methode validerInfos()"); nouveauClient.setNom(nom); nouveauClient.setPrenom(prenom); nouveauClient.setEmail(email); nouveauClient.setFax(fax); nouveauClient.setTelephone(telephone); nouveauClient.setTitre(titre); infosOK = true; return; public void validerAdresse() log.info("Entree dans la methode validerAdresse()"); adresseClient = new Adresse(); BigDecimal cp = new BigDecimal(codepostal) ; adresseClient.setCodepostal(cp); adresseClient.setDepartement(departement); adresseClient.setNumero(numero); adresseClient.setRue(rue); adresseClient.setVille(ville); adresseClient.setPays(pays); nouveauClient.setAdresseFk(adresseClient); adresseOK=true; return; public void validerInscription() log.info("Entree dans la methode validerInscription()"); try clientDAO.save(nouveauClient); succes = "Inscription réussie !"; catch (Exception e )

Le formulaire est développé dans une vue unique, la portée viewScope est donc adaptée dans ce cas. L’inscription se fait en un wizard à trois étapes. Chaque étape est délimitée par un composant <h:panelGrid> dont l’affichage est conditionné grâce à l’attribut rendered. Si la valeur de cet attribut est évaluée à true alors le composant est affiché. L’affichage du formulaire suivant s’affiche si l’utilisateur a correctement rempli les champs. Le remplissage correct du dernier formulaire entraîne l’insertion en base des données du client et l’affichage d’un message de succès sans rafraîchir la page via le tag <f:ajax>.

Voici le code de la page inscription.xhtml :

<?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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui=" http://java.sun.com/jsf/Facelets"> <h:head> <title><h:outputText value="Inscription"/></title> <link rel="stylesheet" type="text/css" href="../css/styles.css" /> </h:head> <center>

- 48 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 297: Ejb Jsf Struts Flex Jasper

<f:view > <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form id="Form1" > <body> <h2> Inscription </h2> <h4>Complétez les formulaires suivants : </h4> <h:panelGrid columns="2" id="loginpwd" bgcolor="DarkKhaki" border="1"> <f:facet name="header" > <h:outputText value="Etape 1 : Login et mot de passe"/> </f:facet> <h:outputText value="Login: *" /> <h:inputText id="login" value="#leClient.login" required="true"/> <h:outputText value="Password: *" /> <h:inputText id="pwd" value="#leClient.password" required="true"/> <f:facet name="footer"> <h:panelGroup style="display:block; text-align:center"> <h:commandButton id="submitLoginPwd" value="Valider" action="#leClient.validerLoginPwd" /> </h:panelGroup> </f:facet> </h:panelGrid> <br /> <h:panelGrid columns="2" id="informations" bgcolor="DarkKhaki" rendered="#leClient.loginPwdOK" border="1"> <f:facet name="header" > <h:outputText value="Etape 2 : Informations"/> </f:facet> <h:outputText value="Nom: * " /> <h:inputText value="#leClient.nom" required="true" /> <h:outputText value="Prenom: * " /> <h:inputText value="#leClient.prenom" required="true"/> <h:outputText value="Titre: * " /> <h:inputText value="#leClient.titre" required="true"/> <h:outputText value="Email: * " /> <h:inputText value="#leClient.email" required="true"/> <h:outputText value="Telephone: * " /> <h:inputText value="#leClient.telephone" required="true"/> <h:outputText value="Fax: * " /> <h:inputText value="#leClient.fax" required="true"/> <f:facet name="footer"> <h:panelGroup style="display:block; text-align:center"> <h:commandButton id="submitInfos" value="Valider" action="#leClient.validerInfos" /> </h:panelGroup> </f:facet> </h:panelGrid> <br /> <h:panelGrid columns="2" id="infosAdresse" bgcolor="DarkKhaki" rendered="#leClient.infosOK" border="1"> <f:facet name="header" > <h:outputText value="Etape 3 : Adresse"/> </f:facet> <h:outputText value="Code postal: * " /> <h:inputText value="#leClient.codepostal" required="true"/> <h:outputText value="Departement: * " /> <h:inputText value="#leClient.departement" required="true"/> <h:outputText value="Numero: * " /> <h:inputText value="#leClient.numero" required="true"/> <h:outputText value="Pays: * " /> <h:inputText value="#leClient.pays" required="true"/> <h:outputText value="Rue: * " />

- 49 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 298: Ejb Jsf Struts Flex Jasper

<h:inputText value="#leClient.rue" required="true"/> <h:outputText value="Ville: * " /> <h:inputText value="#leClient.ville" required="true"/> <f:facet name="footer"> <h:panelGroup style="display:block; text-align:center"> <h:commandButton id="submitAdresse" value="Valider" action="#leClient.validerAdresse" /> </h:panelGroup> </f:facet> </h:panelGrid> <br /> <h:panelGroup style="display:block; text-align:center" rendered="#leClient.adresseOK"> <h:commandButton id="submitInscription" value="Valider l’inscription" action="#leClient.validerInscription" > <f:ajax execute="@form " render=":Form1:finInscription" /> </h:commandButton> </h:panelGroup> <h2><h:outputText value="#leClient.succes" id="finInscription" /></h2> <h:messages /> </body> </h:form> </f:view> </center> </html>

Lorsque le nouveau client entre un mauvais login et un mauvais mot de passe, les messages définis dans le managed bean à l’intérieur des annotations sur les propriétés correspondantes sont affichées :

Et voici la page telle qu’elle est restituée lorsque tous les formulaires sont correctement remplis et que l’insertion en base s’est déroulée sans exception :

- 50 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 299: Ejb Jsf Struts Flex Jasper

Enfin il faut noter la possibilité d’utiliser trois nouveaux tags :

<f:validateRequired> : pour vérifier si une valeur est vide.

<f:validateRegex> : ce tag fournit de la validation basée sur des expressions régulières. La validation de l’email peut donc s’écrire de la façon suivante :

<h:inputText value="#leClient.email" required="true" > <f:validateRequired/> <f:validateRegex pattern=".+@.+\\.[a-z]+"/> </h:inputText>

<f:validateBean> : pour spécifier que la validation de la valeur du tag est déléguée à l’API de validation des beans (JSR­303). L’attribut validationGroups déclare les classes qui contiennent les contraintes qui doivent être appliquées.

12. Interface d’administration

L’interface d’administration permet de gérer les clients et les articles. Les permissions sont les mêmes que celles qui sont appliquées dans la version Struts 2 du client.

- 51 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 300: Ejb Jsf Struts Flex Jasper

Voici la page de gestion des clients, gestionClients.xhtml, qui affiche la liste des clients présents en base :

L’ajout et la modification d’un client sont gérés dans le même écran et la suppression d’un client est faite de façon asynchrone (l’écran n’est pas rafraîchi) grâce à Ajax, comme illustré dans le schéma suivant :

- 52 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 301: Ejb Jsf Struts Flex Jasper

Voici le code de la page sauverClient.xhtml :

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/templateAdmin.xhtml"> <ui:define name="titre"> <h:outputText value="Modification / Création d’un client"/> </ui:define> <ui:define name="contenu"> <center> <f:view> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form id="FormSauverClient"> <div align="right" > <h:graphicImage url="/images/utilisateur.gif" /><br /> <h:outputText value="#lesMessages.general_nom" /> : <h:outputText value="#sessionScope.loginBean.admin.nom" render="#not empty sessionScope.loginBean.admin.nom" /> <h:outputText value="#sessionScope.loginBean.gest.nom" render="#not empty sessionScope.loginBean.gest.nom" /> <br/>

- 53 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 302: Ejb Jsf Struts Flex Jasper

<h:outputText value="#lesMessages.general_prenom" />: <h:outputText value="#sessionScope.loginBean.admin.prenom" render="#not empty sessionScope.loginBean.admin.prenom" /> <h:outputText value="#sessionScope.loginBean.gest.prenom" render="#not empty sessionScope.loginBean.gest.prenom" /> </div> <h2><h:outputText value="Création" rendered="#administrationBean.render1" /> </h2> <h2><h:outputText value=" Modification de client" rendered="#administrationBean.render2" /> </h2> <h:panelGrid columns="2" cellpadding="3px"> <h:outputLabel for="leNom" value="Nom"/> <h:inputText type="text" id="leNom" value="#administrationBean.clientCourant.nom" /> <h:outputLabel for="lePrenom" value="Nom"/> <h:inputText id="lePrenom" value="#administrationBean.clientCourant.prenom"/> <h:outputLabel for="lemail" value="Email"/> <h:inputText id="lemail" value="#administrationBean.clientCourant.email"/> <h:outputLabel for="lelogin" value="Login"/> <h:inputText id="lelogin" value="#administrationBean.clientCourant.login"/> <h:outputLabel for="lePassword" value="Password"/> <h:inputText id="lePassword" value="#administrationBean.clientCourant.password"/> <h:outputLabel for="leTitre" value="Titre"/> <h:inputText id="leTitre" value="#administrationBean.clientCourant.titre"/> <h:outputLabel for="leFax" value="Fax"/> <h:inputText id="leFax" value="#administrationBean.clientCourant.fax"/> <h:outputLabel for="leTelephone" value="Telephone"/> <h:inputText id="leTelephone" value="#administrationBean.clientCourant.telephone"/> <h:commandButton value="Créer" action="#administrationBean.sauverClient" disabled="#administrationBean.desactive1" /> <h:commandButton value="Modifier" action="#administrationBean.sauverClient" disabled="#administrationBean.desactive2" /> </h:panelGrid> </h:form> <h4> <h:messages showDetail ="true" /></h4> </f:view> </center> </ui:define> </ui:composition>

Les attributs disabled des tags <h:commandButton> évaluent des booléens pour décider de l’activation ou de la désactivation des boutons. Ces attributs sont settés dans le managed bean AdministrationBean pour activer le bouton Créer et désactiver le bouton Modifier lorsque l’utilisateur souhaite créer un client, et vice versa, lorsqu’il souhaite modifier un client.

- 54 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 303: Ejb Jsf Struts Flex Jasper

Comme pour le client Struts 2, le stateful session bean contient les méthodes métier d’insertion, suppression, recherche et modification de clients. Voici le code du managed bean AdministrationBean :

package com.eni.dvtejb.clientJSF2.beans; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.ejb.EJB; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.context.FacesContext; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import org.apache.log4j.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Produit; import com.eni.dvtejb.metier.entities.Stock; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.sessions.ArticleDAOBeanRemote; import com.eni.dvtejb.metier.sessions.UtilisateurDAOBeanRemote; @ManagedBean @SessionScoped public class AdministrationBean private static final Logger log = Logger.getLogger(AdministrationBean.class); public AdministrationBean() private List listeClients; private List listeArticles; private Client clientCourant;

- 55 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 304: Ejb Jsf Struts Flex Jasper

private Article articleCourant; boolean desactive1; // bouton creation Client boolean desactive2; // bouton modification Client boolean render1; // Titre creation Client boolean render2; // Titre modification Client // getters et setters @EJB(name="VenteEnLigne/UtilisateurDAOBean/remote") private UtilisateurDAOBeanRemote utilisateurDAO; DataModel<Client> listeClientsModel = new ListDataModel<Client>(); public DataModel<Client> getListeClientsModel() // Alimentation du DataModel à partir d’une liste listeClientsModel.setWrappedData(getListeClients()); return listeClientsModel; public void setListeClientsModel( DataModel<Client> listeClientsModel ) this.listeClientsModel = listeClientsModel; // Retourne la liste des clients public void rechercherClients() List<Utilisateur> listeUtilisateurs = new ArrayList<Utilisateur>(); listeUtilisateurs = utilisateurDAO.rechercherTous(); listeClients = listeUtilisateurs; //Suppression d’un client public void supprimerClient() FacesContext context = FacesContext.getCurrentInstance(); Map<String, String> params = context.getExternalContext().getRequestParameterMap(); String numeroId = params.get("id"); long l = Long.valueOf(numeroId); utilisateurDAO.supprimer(l); //Modification d’un client public String modifier() clientCourant = (Client)listeClientsModel.getRowData(); desactive1=true; desactive2=false; render1=false; render2=true; return "sauverClient?faces-redirect=true"; //Ajout d’un client public String ajouter() clientCourant = new Client(); desactive1=false; desactive2=true; render1=true; render2=false; return "sauverClient?faces-redirect=true"; // Ajout / Modification d’un client public void sauverClient() utilisateurDAO.sauver(clientCourant); FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès -", "Opération effectuée

- 56 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 305: Ejb Jsf Struts Flex Jasper

avec succès !"));

La liste des clients est encapsulée dans le modèle de liste ListDataModel. Pour illustrer les avantages de ce modèle, les deux méthodes modifier() et supprimerClient() ont deux approches différentes pour récupérer le client sélectionné. Dans la méthode modifier(), la méthode getRowData() de la classe ListDataModel renvoie la ligne qui a été sélectionnée, il suffit alors de la caster vers le type de l’objet, c’est­à­dire Client. Il n’y a pas besoin de passer l’ID du client en paramètre dans la page XHTML et d’aller le récupérer dans les paramètres de la requête, comme c’est le cas dans la méthode supprimerClient().

Enfin, lorsque la navigation est implicite, comme c’est le cas dans ce managed bean, la redirection peut se faire avec le paramètre faces­redirect égal à true. Ce paramètre présente l’avantage de mettre à jour l’URL de la page.

La gestion des articles est similaire à la gestion des clients.

- 57 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqaSwvFlH82ICwA=-enidentnumber

Page 306: Ejb Jsf Struts Flex Jasper

Introduction à JasperReports et à iReport

Pour prendre des décisions avisées, les utilisateurs d’une application Web, qu’ils soient clients, gestionnaires ou administrateurs, doivent disposer de données à jour et détaillées. L’informatique décisionnelle (ou BI : Business Intelligence) a donc une place importante dans les applications et l’outil décisionnel de génération de rapports en est souvent un composant essentiel. Sa capacité d’intégration est primordiale. Dans ce chapitre, vous allez voir la mise en œuvre du framework JasperReports dans l’application afin de générer des rapports. Lorsque le client consulte les détails d’un article, il a la possibilité de générer à la volée une fiche des détails dans un format PDF. Des statistiques sur les articles par produit et catalogue présents en base sont également mises à disposition des gestionnaires et administrateurs grâce à des graphiques (diagramme et camembert) et des tableaux croisés dynamiques.

1. Le framework JasperReports

JasperReports est un framework Open Source de reporting très populaire, développé en Java et qui a été initié en 2001 par Teodor Danciu. C’est devenu aujourd’hui une solution mâture et fonctionnellement riche pour intégrer à moindre coût un module de reporting à une application. JasperReports permet de générer des fichiers aux formats PDF, XML, HTML, Excel, Word, OpenOffice (ODF) ou CSV à partir de données provenant de sources diverses : une base de données relationnelle (par exemple Oracle) ou multidimensionnelle/OLAP (par exemple Microsoft SQL Server Analysis Services), de JavaBeans, d’une HashMap, de requêtes EJBQL, d’un annuaire LDAP, de fichiers XML ou CSV. Il peut être intégré dans une application Java EE pour fournir au développeur un moyen puissant de créer facilement des rapports personnalisés contenant des données de tout type : textes, images, diagrammes, graphes… Ces rapports peuvent être par exemple des factures, des formulaires ou bien encore des tableaux de bords.

Le framework JasperReports se compose d’une librairie Java qu’il faut mettre dans le CLASSPATH. La compilation du fichier de design .jrxml est effectuée par la méthode compileReport() de la classe net.sf.jasperreports.engine.JasperManager. La compilation produit en sortie un fichier binaire dont l’extension est .jasper. Le fichier de design est transformé en objet de type net.sf.jasperreports.engine.JasperReport et est sérialisé. Ce fichier sérialisé est ensuite utilisé pour remplir et produire un rapport avec des données.

2. L’outil de design iReport

iReport est un outil de design de type WYSIWYG (What You See Is What You Get) qui a été créé en 2002 par Giulio Toffoli. Il s’adresse aux développeurs et aux utilisateurs qui utilisent la librairie JasperReports pour créer des rapports. Il est développé en Java et permet d’éditer de façon visuelle des rapports au format XML contenant des diagrammes, des images, des sous­rapports, etc. Il existe une version standalone et une version plugin pour Eclipse. Modifier les fichiers .JRXML à la main n’est pas une opération facile, ce qui fait d’iReport un outil indispensable pour la mise en forme des rapports.

La figure suivante montre la template générale qui correspond à la structure d’un fichier .jrxml vide avec une seule colonne et créé dans iReport :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpmA/1tmH82ICwA=-enidentnumber

Page 307: Ejb Jsf Struts Flex Jasper

Voici une description des sections (ou bandes) qui composent cette template dans iReport. Les tags XML correspondants dans le fichier .JRXML sont indiqués entre parenthèses :

Title (<title>) : le titre apparaît une seule fois, au début du rapport.

Page Header (<pageHeader>) : cette section apparaît au début de chaque page. C’est l’entête de la page.

Column Header (<columnHeader>) : l’en­tête de colonne liste les noms des champs à afficher.

Detail (<detail>) : les valeurs des champs de la source de données sont affichées ici.

Column Footer (<columnFooter>) : le pied de colonne qui peut servir à afficher des sommes sur ces champs.

Page Footer (<pageFooter>) : c’est le pied de page qui apparaît au bas de chaque page et peut afficher un compteur de pages.

Summary (<summary>) : affiche des données déduites de celles affichées dans la section Detail. Par exemple, un graphe sous la forme d’un camembert.

Des sections Groups peuvent être ajoutées pour regrouper les données et créer des ruptures dans le rapport. Dans ce cas, une section d’en­tête de groupe et une section de pied de groupe sont insérées autour de la section Detail.

Ce fichier peut également être créé avec l’API JasperReports à l’aide de la classe net.sf.jasperreports.engine.design.JasperDesign mais sa création est grandement facilitée avec l’outil iReport. En effet, iReport dispose d’un wizard (assistant de création), de templates, d’une palette contenant des éléments qu’il est possible d’insérer dans le fichier par un simple drag and drop, de modes design, XML et preview, etc.

Enfin voici les principales expressions qui font partie de la syntaxe d’un fichier .JRXML et qui font référence à des objets du rapport :

$PnomParam : le nom d’un paramètre du rapport. Les paramètres permettent de transmettre des données ne faisant pas partie des sources de données.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpmA/1tmH82ICwA=-enidentnumber

Page 308: Ejb Jsf Struts Flex Jasper

$VnomVar : le nom d’une variable. Les variables permettent de stocker des résultats d’opérations sur des calculs tels que des moyennes (average), des sommes (sum), des variances (variance)…

$FnomChamp] : le nom d’un champ (Field). Les champs correspondent aux champs faisant partie du résultat d’une requête.

Le schéma suivant illustre les différentes étapes nécessaires à la génération d’un rapport, en utilisant le framework JasperReports et l’outil de design iReport :

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MpmA/1tmH82ICwA=-enidentnumber

Page 309: Ejb Jsf Struts Flex Jasper

Les EJB 3 en tant que source de données

Pour montrer l’utilisation des EJB avec JasperReports, les rapports élaborés dans les sous­chapitres suivants utilisent tous des requêtes EJBQL en tant que source de données.

1. Génération d’un rapport depuis une classe Java

L’installation consiste à récupérer la librairie JasperReports et à installer l’outil iReport. Vous pouvez télécharger la librairie JasperReports ainsi que iReport à l’URL du site officiel : http://jasperforge.org. La version de la librairie JAR utilisée dans cet ouvrage est la 3.6.1 (mise en production en octobre 2009) : jasperreports­3.6.1.jar dont la taille est d’environ 2,5 MB. Il existe également une distribution plus complète, jasperreports­3.6.1­project.zip, qui inclus les sources, la documentation et des exemples.

iReport est disponible à la même adresse URL. Récupérez le fichier iReport­nb­3.6.1­windows­installer.exe. La taille de ce fichier est d’environ 72 MB. Lancez l’exécutable et suivez la procédure d’installation. Prévoyez environ 111 MB pour l’installation de l’outil. iReport requiert la version 1.5 (au minimum) du JDK.

De manière générale, les étapes en vue de la création d’un rapport au format PDF sont les suivantes :

1 ­ Création d’une template de rapport JRXML à l’aide de l’outil iReport.

2 ­ Insertion de la requête EJBQL dans le fichier JRXML.

3 ­ Compilation du fichier .jrxml et génération du fichier binaire .jasper.

4 ­ Génération du fichier de sortie au format PDF.

Le schéma suivant illustre ces étapes :

a. Étape 1 : création d’une template de rapport à l’aide de iReport

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 310: Ejb Jsf Struts Flex Jasper

La template de rapport correspond à un fichier dont l’extension est .jrxml. C’est un fichier au format XML. Il peut être codé à la main mais l’utilisation de l’outil graphique iReport facilite grandement sa construction et donc le design du document à générer au final.

Démarrez iReport :

Outil graphique iReport au démarrage

Créez un nouveau rapport en suivant le wizard. Un rapport est un fichier .jrxml. Ce fichier est compilé en un fichier .jasper. Nommez ce fichier ListeArticlesRapport.jrxml. Le premier rapport est développé dans le projet VenteEnLigneClient. Dans Eclipse, il est nécessaire pour pouvoir compiler les fichiers .JRXML d’ajouter au minimum les librairies suivantes dans le build path du projet :

jasperreports­3.6.1\lib\jasperreports­3.6.1.jar

jasperreports­3.6.1\lib\groovy­all­1.5.5.jar

jasperreports­3.6.1\lib\iText­2.1.0.jar pour la création des fichiers au format PDF

jasperreports­3.6.1\lib\jcommon­1.0.15.jar

Ces librairies sont disponibles dans le répertoire /lib de la distribution de JasperReports.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 311: Ejb Jsf Struts Flex Jasper

La librairie Open Source iText peut également être téléchargée séparément à l’adresse http://www.lowagie.com/iText.

Le premier rapport affiche la liste des articles présents dans le magasin sous forme de tableau dans un fichier PDF. Le design de la page est assez intuitif, ce qui est l’un des avantages de JasperReports par rapport à ses concurrents. Une palette d’éléments graphiques est disponible à droite. Un drag and drop d’un élément permet d’afficher ce dernier sur la page en cours de création. Par exemple, l’élément Static Text est utilisé pour afficher, au niveau de la bande COLUMN HEADER, des labels correspondant aux titres des colonnes de la page : Article ID, Catalogue, Produit, Article, Prix, Stock.

b. Étape 2 : création de la requête EJBQL

Pour cet exemple, vous pouvez reprendre la requête déjà utilisée précédemment et qui retourne la liste des articles (nom, prix, stock, id) ainsi que les produits et catalogues correspondants. Dans le mode XML, insérez la requête suivante à l’intérieur du tag <queryString>, sous la ligne <parameter name="TitreRapport" class="java.lang.String"/> :

queryString language="ejbql"> <![CDATA[select c.nom, p.nom, a.nom, a.articleid, a.prix, s.quantite from Catalogue c, Produit p, Article a, Stock s where c.catalogueid = p.catalogueFk

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 312: Ejb Jsf Struts Flex Jasper

and p.produitid = a.produitFk and a.stockFK = s.stockid]]> </queryString> <field name="COLUMN_1" class="java.lang.String"/> <field name="COLUMN_2" class="java.lang.String"/> <field name="COLUMN_3" class="java.lang.String"/> <field name="COLUMN_4" class="java.lang.Long"/> <field name="COLUMN_5" class="java.lang.Double"/> <field name="COLUMN_6" class="java.math.BigDecimal"/>

L’attribut ejbql indique à JasperReports d’utiliser la Java Persistence API pour récupérer les données. Les tags <field name> définissent les noms des champs retournés par la requête et qui sont réutilisés dans le corps du rapport. Les noms des champs correspondant aux noms des colonnes retournées doivent suivre une règle de nommage particulière : ils doivent être nommés COLUMN_X, où X est le numéro de la colonne, en commençant par 1. Le champ COLUMN_1 correspond donc à la colonne c.nom, le champ COLUMN_2 à la colonne p.nom, etc.

Vous pouvez également insérer la requête avec l’éditeur de requêtes inclus dans iReport. Dans le menu du rapport, cliquez sur l’icône « Report Query », à droite du bouton « Preview ». L’éditeur de requête s’affiche avec la requête précédemment insérée dans le mode XML.

Le mode XML permet de visualiser le contenu du fichier ListeArticlesRapport.jrxml dans son intégralité :

<?xml version="1.0" encoding="UTF-8"?> <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="ListeDesArticles" language="groovy" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20"> <style name="Sans_Normal" isDefault="true" fontName="DejaVu Sans" fontSize="12" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false"/> <style name="Sans_Bold" isDefault="false" fontName="DejaVu Sans" fontSize="12" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false"/> <parameter name="TitreRapport" class="java.lang.String"/>

<queryString language="ejbql">

<![CDATA[select c.nom, p.nom, a.nom, a.articleid, a.prix,

s.quantite from Catalogue c, Produit p, Article a, Stock s

where c.catalogueid = p.catalogueFk

and p.produitid = a.produitFk

and a.stockFK = s.stockid]]>

</queryString> <field name="COLUMN_1" class="java.lang.String"/> <field name="COLUMN_2" class="java.lang.String"/>

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 313: Ejb Jsf Struts Flex Jasper

<field name="COLUMN_3" class="java.lang.String"/> <field name="COLUMN_4" class="java.lang.Long"/> <field name="COLUMN_5" class="java.lang.Double"/> <field name="COLUMN_6" class="java.math.BigDecimal"/> <background> <band splitType="Stretch"/> </background> <title> <band height="21" splitType="Stretch"> <textField> <reportElement style="Sans_Bold" mode="Opaque" x="0" y="0" width="555" height="15" forecolor="#FFFFFF" backcolor="#009999"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.lang.String"><![CDATA[$PTitreRapport]]></textFieldExpress ion> </textField> </band> </title> <pageHeader> <band height="35" splitType="Stretch"> <staticText> <reportElement style="Sans_Bold" mode="Opaque" x="454" y="0" width="60" height="35" forecolor="#FFFFFF" backcolor="#FF0033"/> <box leftPadding="10" rightPadding="10"/> <textElement textAlignment="Center" verticalAlignment="Middle"> <font isItalic="true" isUnderline="true"/> </textElement> <text><![CDATA[PRIX]]></text> </staticText> <staticText> <reportElement style="Sans_Bold" mode="Opaque" x="262" y="0" width="192" height="35" forecolor="#FFFFFF" backcolor="#333333"/> <box leftPadding="10" rightPadding="10"/> <textElement textAlignment="Center" verticalAlignment="Middle"> <font isItalic="true" isUnderline="true"/> </textElement> <text><![CDATA[ARTICLE]]></text> </staticText> <staticText> <reportElement style="Sans_Bold" mode="Opaque" x="0" y="0" width="47" height="35" forecolor="#FFFFFF" backcolor="#FF0033"/> <textElement textAlignment="Center" verticalAlignment="Middle"> <font isItalic="true" isUnderline="true"/> </textElement> <text><![CDATA[Article ID]]></text> </staticText> <staticText> <reportElement style="Sans_Bold" mode="Opaque" x="141" y="0" width="121" height="35" forecolor="#FFFFFF" backcolor="#FF0033"/> <textElement textAlignment="Center" verticalAlignment="Middle"> <font isItalic="true" isUnderline="true"/> </textElement> <text><![CDATA[Produit]]></text> </staticText> <staticText>

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 314: Ejb Jsf Struts Flex Jasper

<reportElement style="Sans_Bold" mode="Opaque" x="514" y="0" width="41" height="35" forecolor="#FFFFFF" backcolor="#333333"/> <textElement textAlignment="Center" verticalAlignment="Middle"> <font isItalic="true" isUnderline="true" isStrikeThrough="false"/> </textElement> <text><![CDATA[Stock]]></text> </staticText> <staticText> <reportElement style="Sans_Bold" mode="Opaque" x="47" y="0" width="94" height="35" forecolor="#FFFFFF" backcolor="#333333"/> <textElement textAlignment="Center" verticalAlignment="Middle"> <font isItalic="true" isUnderline="true"/> </textElement> <text><![CDATA[Catalogue]]></text> </staticText> </band> </pageHeader> <columnHeader> <band height="9" splitType="Stretch"/> </columnHeader> <detail> <band height="23" splitType="Stretch"> <textField> <reportElement style="Sans_Bold" mode="Opaque" x="47" y="0" width="94" height="20" forecolor="#FFFFFF" backcolor="#333333"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.lang.String"><![CDATA[$FCOLUMN_1]]></textFieldExpression> </textField> <textField> <reportElement style="Sans_Bold" mode="Opaque" x="141" y="0" width="121" height="20" forecolor="#FFFFFF" backcolor="#FF0033"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.lang.String"><![CDATA[$FCOLUMN_2]]></textFieldExpression> </textField> <textField> <reportElement style="Sans_Bold" mode="Opaque" x="262" y="0" width="192" height="20" forecolor="#FFFFFF" backcolor="#333333"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.lang.String"><![CDATA[$FCOLUMN_3]]></textFieldExpression> </textField> <textField> <reportElement style="Sans_Bold" mode="Opaque" x="0" y="0" width="47" height="20" forecolor="#FFFFFF" backcolor="#FF0033"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.lang.Long"><![CDATA[$FCOLUMN_4]]></textFieldExpression> </textField> <textField> <reportElement style="Sans_Bold" mode="Opaque" x="454" y="0" width="60" height="20" forecolor="#FFFFFF" backcolor="#FF0033"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.lang.Double"><![CDATA[$FCOLUMN_5]]></textFieldExpression> </textField> <textField>

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 315: Ejb Jsf Struts Flex Jasper

<reportElement style="Sans_Bold" mode="Opaque" x="514" y="0" width="41" height="20" forecolor="#FFFFFF" backcolor="#333333"/> <textElement textAlignment="Center"/> <textFieldExpression class="java.math.BigDecimal"><![CDATA[$FCOLUMN_6]]></textFieldExpress ion> </textField> </band> </detail> <columnFooter> <band height="33" splitType="Stretch"/> </columnFooter> <pageFooter> <band height="41" splitType="Stretch"/> </pageFooter> <summary> <band height="35" splitType="Stretch"/> </summary> </jasperReport>

c. Étape 3 : compilation du fichier .jrxml et génération du fichier binaire .jasper

Créez le package com.eni.dvtejb.jasper dans le projet VenteEnLigneClient. Sauvegardez le fichier ListeArticlesRapport.jrxml dans ce package. La classe de test ListeArticlesJasper.java suivante permet de compiler le fichier .jrxml et de produire le fichier PDF qui liste les articles présents en base. L’unité de persistance est VenteEnLigneClientJavaSE. Elle est définie dans le fichier META­INF/persistence.xml. Il faut modifier ce fichier avant de lancer le test : le tag <non­jta­data­source> doit être vide.

package com.eni.dvtejb.jasper; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperRunManager; import net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory; public class ListeArticlesJasper public static void main(String[] args) // Compilation String resultat = compileReport("K:/ENI/DeveloppementEJB3/workspaceGalileo/VenteEnLigne Client/appClientModule/com/eni/dvtejb/jasper/", "ListeArticlesRapport"); try long start = System.currentTimeMillis(); // création de l’entity manager factory pour la connexion avec la base de données EntityManagerFactory emf = Persistence.createEntityManagerFactory("VenteEnLigneClientJavaSE", new

HashMap()); EntityManager em = emf.createEntityManager(); try Map parameters = getParameters(em);

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 316: Ejb Jsf Struts Flex Jasper

JasperRunManager.runReportToPdfFile("K:/ENI/DeveloppementEJB3/workspa ceGalileo/VenteEnLigneClient/appClientModule/com/eni/dvtejb/jasper/List eArticlesRapport.jasper", parameters); em.close(); System.err.println("Durée de génération du fichier PDF: " + (System.currentTimeMillis() - start)); finally if (em.isOpen()) em.close(); if (emf.isOpen()) emf.close(); catch (JRException e) e.printStackTrace(); catch (Exception e) e.printStackTrace(); // Paramètres du rapport private static Map getParameters(EntityManager em) Map parameters = new HashMap(); parameters.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MANAGER, em); parameters.put("TitreRapport", "Liste des articles"); return parameters; // Compilation du fichier .jrxml en un binaire .jasper public static String compileReport(String path, String reportName) try // compileReportToFile(fichier source, fichier destination) JasperCompileManager.compileReportToFile(path+reportName+".jrxml", path+reportName+".jasper"); catch(Exception e) e.printStackTrace(); return "erreur"; return "succes";

Une instance EntityManager est stockée dans la Hashmap qui est fournie en paramètre de la méthode runReportToPdfFile(). Une valeur est affectée au paramètre TitreRapport et est également ajoutée dans cette Hashmap. Les paramètres permettent de transmettre au rapport des données qui ne sont pas stockées en base de données.

JasperReports a introduit les interfaces net.sf.jasperreports.engine.query.JRQueryExecuterFactory et net.sf.jasperreports.engine.query.JRQueryExecuter pour pouvoir ajouter des langages de requêtes. Il suffit de les implémenter. C’est le cas de la classe net.sf.jasperreports.engine.query.JRJpaQueryExecuter. Elle implémente l’interface JRQueryExecuter et a été développée pour supporter les requêtes JPA / EJBQL.

Le nombre de lignes retournées par une requête EJBQL peut être contrôlé grâce au champ PROPERTY_JPA_QUERY_PAGE_SIZE de la classe JRJpaQueryExecuter. La pagination est donc facilitée.

Une fois compilé, le rapport est rempli avec les données et un fichier PDF est généré.

d. Étape 4 : génération du fichier de sortie au format PDF

La méthode runReportToPdfFile() de la classe net.sf.jasperreports.engine.JasperRunManager permet de produire le fichier de sortie au format PDF.

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 317: Ejb Jsf Struts Flex Jasper

2. Génération d’un rapport depuis un client Web et ajout de graphiques

L’outil de reporting doit permettre de présenter les données d’une manière synthétique, détaillée ou graphique. JasperReports permet également de générer des graphes en tout genre (camemberts, diagrammes 3D, courbes, baromètres…). Les réalisations d’un rapport qui affiche les prix des articles par catalogue sous la forme d’un diagramme horizontal et d’un rapport qui affiche la répartition des catalogues selon la quantité d’articles qu’ils contiennent dans un graphique sous forme de camembert sont là encore facilitées par l’outil iReport.

Ajoutez les librairies jfreechart­1.0.12.jar et jcommon­1.0.15.jar dans le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\lib. Vous pouvez les trouver dans le répertoire \lib de la distribution de JasperReports. JFreeChart est la librairie utilisée dans JasperReports pour générer des graphiques.

À l’aide d’iReport, créez la template RapportGraphique.jrxml et sauvegardez­la dans le répertoire WebContent\rapports\ du projet VenteEnLigneWeb. Seules les sections Title et Summary sont utilisées dans ce rapport.

La requête EJBQL utilisée pour ce rapport est la suivante :

<queryString language="ejbql"> <![CDATA[select a.prix, a.nom, c.nom from Article a, Produit p, Catalogue c where a.produitFk = p.produiti and p.catalogueFk = c.catalogueid order by c.nom]]> </queryString> <field name="COLUMN_1" class="java.math.BigDecimal"/> <field name="COLUMN_2" class="java.lang.String"/> <field name="COLUMN_3" class="java.lang.String"/>

Cette requête va servir à alimenter le diagramme. Pour construire ce diagramme, allez dans la palette, faites un glisser­déposer de l’élément Chart vers la section Summary de la template :

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 318: Ejb Jsf Struts Flex Jasper

Ensuite, dans le template, faites un clic droit sur cet élément et sélectionnez Chart Data. Les propriétés du diagramme s’affichent. Sélectionnez l’onglet Details.

Cliquez sur le bouton Add.

Il faut maintenant préciser les valeurs des champs qui, dans le diagramme, vont s’afficher dans l’axe des abscisses (Category Expression) et dans l’axe des ordonnées (Value Expression). Le regroupement par catalogue est indiqué

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 319: Ejb Jsf Struts Flex Jasper

dans le champ Series expression.

Saisissez les valeurs suivantes :

Dans les propriétés du diagramme, changez l’orientation pour qu’elle soit horizontale au lieu de verticale. Pour réaliser un graphique de type camembert, la procédure est identique à celle utilisée pour le diagramme. Les valeurs des paramètres à renseigner sont affichées sur la figure suivante :

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 320: Ejb Jsf Struts Flex Jasper

Le champ Key expression permet d’identifier une part (slice) de camembert au niveau de la légende du bas.

Le champ Value expression est la valeur utilisée pour quantifier les parts.

Le champ Label expression correspond aux labels des parts.

Le développement de la template du rapport sous iReport est maintenant terminé.

La génération du rapport depuis un client Web favorise la consultation des données.

Dans le projet VenteEnLigneWeb, ajoutez les librairies suivantes dans le répertoire /WEB­INF/lib :

jasperreports­3.6.1.jar ;

groovy­all­1.5.5.jar ;

iText­2.1.0.jar pour la génération des rapports au format PDF.

Ces librairies se trouvent dans le répertoire /lib de la distribution JasperReports.

La JSP VenteEnLigneWeb\WebContent\RapportGraphique.jsp contient un bouton qui appelle la servlet RapportGraphiqueJasperServlet qui génère le rapport :

<form action="RapportGraphiqueJasperServlet"> <h1> Rapports graphiques </h1> <input type="submit" value="Générer le rapport" /> </form>

Le code de la servlet RapportGraphiqueJasperServlet.java est le suivant :

package com.eni.dvtejb.web; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter;

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 321: Ejb Jsf Struts Flex Jasper

import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRExporterParameter; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.export.JRPdfExporter; import net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory; import net.sf.jasperreports.j2ee.servlets.ImageServlet; import org.apache.log4j.Logger; public class RapportGraphiqueJasperServlet extends HttpServlet private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(RapportGraphiqueJasperServlet.class); public RapportGraphiqueJasperServlet() super(); public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException ServletContext context = this.getServletConfig().getServletContext(); // 1.Compilation du fichier .jrxml en .jasper try JasperCompileManager.compileReportToFile(context.getRealPath("/rappo rts/RapportGraphique.jrxml")); catch (JRException e) ... // 2.Remplissage du fichier .jasper avec les données de la base. Genere un fichier .jrprint JasperPrint jasperPrint = null; EntityManagerFactory emf = Persistence.createEntityManagerFactory("VenteEnLigneModuleEJB", new HashMap()); EntityManager em = emf.createEntityManager(); try Map parametresMap = new HashMap(); parametresMap.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MAN AGER, em); String reportFileName = context.getRealPath("/rapports/RapportGraphique.jasper"); jasperPrint = JasperFillManager.fillReport(reportFileName,

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 322: Ejb Jsf Struts Flex Jasper

parametresMap); log.info("-------------------------Fin remplissage du fichier et generation du fichier .jrprint"); catch (JRException e) e.printStackTrace(); finally if (em.isOpen()) em.close(); if (emf.isOpen()) emf.close(); //3. Export du fichier .jrprint au format PDF ServletOutputStream out2 = response.getOutputStream(); try JRPdfExporter exporterPDF = new JRPdfExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporterPDF.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); jasperPrint.getName(); exporterPDF.setParameter(JRExporterParameter.OUTPUT_STREAM, out2); response.setContentType("application/pdf"); response.setHeader("Content-disposition", "filename=" + "RapportGraphique.pdf;"); exporterPDF.exportReport(); catch (JRException e) ...

Voici le diagramme horizontal et le camembert générés et affichés dans le browser Chrome :

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 323: Ejb Jsf Struts Flex Jasper

3. Utilisation d’une requête EJBQL paramétrée dans un rapport

Les critères des requêtes EJBQL peuvent être dynamiques grâce à l’utilisation de paramètres. Ces paramètres permettent à l’utilisateur de sélectionner les données qu’il souhaite afficher.

La requête utilisée dans le rapport suivant possède un critère dont la valeur est dynamique. En effet, l’utilisateur choisi le type de catalogue et la servlet récupère ce paramètre pour l’ajouter dans la Hashmap qui est passée en paramètre de la méthode fillReport() de remplissage du rapport. Voici le code de la servlet ListeArticlesJasperServlet.java :

package com.eni.dvtejb.web; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRExporterParameter; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.export.JExcelApiExporter; import net.sf.jasperreports.engine.export.JRHtmlExporter; import net.sf.jasperreports.engine.export.JRHtmlExporterParameter; import net.sf.jasperreports.engine.export.JRPdfExporter; import net.sf.jasperreports.engine.export.JRRtfExporter;

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 324: Ejb Jsf Struts Flex Jasper

import net.sf.jasperreports.engine.export.ooxml.JRDocxExporter; import net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory; import net.sf.jasperreports.j2ee.servlets.ImageServlet; import org.apache.log4j.Logger; public class ListeArticlesJasperServlet extends HttpServlet private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(ListeArticlesJasperServlet.class); public ListeArticlesJasperServlet() super(); public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException ServletContext context = this.getServletConfig().getServletContext(); String typeRapport = request.getParameter("typeRapport"); System.out.println("typeRapport vaut : " + typeRapport); String leCatalogue = request.getParameter("leCatalogue"); // 1.Compilation du fichier .jrxml en .jasper try JasperCompileManager.compileReportToFile(context.getRealPath("/rappo rts/ListeArticlesCritereCatalogueRapport.jrxml")); catch (JRException e) // affichage de la stack trace dans le browser ... return; // 2.Remplissage du fichier .jasper avec les données de la base. Genere un fichier .jrprint JasperPrint jasperPrint = null; EntityManagerFactory emf = Persistence.createEntityManagerFactory("VenteEnLigneModuleEJB", new HashMap()); EntityManager em = emf.createEntityManager(); try Map parametresMap = new HashMap(); parametresMap.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MAN AGER, em); parametresMap.put("TitreRapport", "Liste des articles en base"); parametresMap.put("NomCatalogue", leCatalogue); String reportFileName = context.getRealPath("/rapports/ListeArticlesCritereCatalogueRapport.jasper "); jasperPrint = JasperFillManager.fillReport(reportFileName, parametresMap); catch (JRException e) e.printStackTrace(); finally if (em.isOpen()) em.close(); if (emf.isOpen()) emf.close();

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 325: Ejb Jsf Struts Flex Jasper

//3. Export du fichier .jrprint au format choisi : HTML / PDF / Excel / RTF / Word 2007 if ("pageHTML".equals(typeRapport)) PrintWriter out = response.getWriter(); try JRHtmlExporter exporter = new JRHtmlExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporter.setParameter(JRExporterParameter.OUTPUT_WRITER, out); exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, "image?image="); exporter.exportReport(); catch (JRException e) // affichage de la stack trace dans le browser ... if ("pdf".equals(typeRapport)) ServletOutputStream out2 = response.getOutputStream(); try JRPdfExporter exporterPDF = new JRPdfExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporterPDF.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); jasperPrint.getName(); exporterPDF.setParameter(JRExporterParameter.OUTPUT_STREAM, out2); response.setContentType("application/pdf"); response.setHeader("Content-disposition", "filename=" + "listeArticles.pdf;"); exporterPDF.exportReport(); catch (JRException e) // affichage de la stack trace dans le browser ... if ("xls".equals(typeRapport)) ServletOutputStream out2 = response.getOutputStream(); try JExcelApiExporter exporterXLS = new JExcelApiExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporterXLS.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporterXLS.setParameter(JRExporterParameter.OUTPUT_STREAM, out2); response.setContentType("application/msexcel"); response.setHeader("Content-disposition", "filename=" + "listeArticles.xls;"); exporterXLS.exportReport(); catch (JRException e) // affichage de la stack trace dans le browser ... if ("rtf".equals(typeRapport)) ServletOutputStream out2 = response.getOutputStream();

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 326: Ejb Jsf Struts Flex Jasper

try JRRtfExporter exporterRTF = new JRRtfExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporterRTF.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporterRTF.setParameter(JRExporterParameter.OUTPUT_STREAM, out2); response.setContentType("application/rtf"); response.setHeader("Content-disposition", "filename=" + "listeArticles.rtf;"); exporterRTF.exportReport(); catch (JRException e) // affichage de la stack trace dans le browser ... if ("word2007".equals(typeRapport)) ServletOutputStream out2 = response.getOutputStream(); try JRDocxExporter exporterDocx = new JRDocxExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporterDocx.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporterDocx.setParameter(JRExporterParameter.OUTPUT_STREAM, out2); response.setContentType("application/vnd.ms- word.document.12"); response.setHeader("Content-disposition", "filename=" + "listeArticles.docx;"); exporterDocx.exportReport(); catch (JRException e) // affichage de la stack trace dans le browser ...

Cette servlet gère plusieurs types de format (PDF, Excel, RTF, Word 2007, HTML) grâce à des classes qui implémentent l’interface net.sf.jasperreports.engine.JRExporter.

Le tableau suivant liste quelques­unes des implémentations disponibles :

Implémentation de JRExporter Format généré

JRPdfExporter PDF

JExcelApiExporter

JRXlsExporter

Excel

JRRtfExporter RTF

JRDocxExporter Microsoft Word 2007

JRHtmlExporter HTML

JRTextExporter TXT

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 327: Ejb Jsf Struts Flex Jasper

Dans un environnement de production, il est souhaitable qu’un fichier précompilé (avec le suffixe .jasper) soit fourni pour que la compilation n’ait pas lieu à chaque requête et que le rapport soit produit plus rapidement.

Une alternative possible à l’utilisation directe des fichiers intermédiaires (.jrxml, .jasper, .jprint) est l’utilisation des classes qui les représentent (net.sf.jasperreports.engine.design.JasperDesign, net.sf.jasperreports.engine.JasperReport, net.sf.jasperreports.engine.JasperPrint) :

JasperDesign jasperDesign = JRXmlLoader.load(context.getRealPath("/rapports/ListeArticlesCritereCatal ogueRapport.jrxml")); JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); jasperPrint = JasperFillManager.fillReport(jasperReport, parametresMap); JRHtmlExporter exporter = new JRHtmlExporter(); exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporter.exportReport();

Le schéma suivant illustre les classes utilisées lors des différentes phases nécessaires à la génération d’un rapport :

La requête EJBQL insérée dans ce rapport est paramétrée. Le paramètre $PNomCatalogue est de type java.lang.String et correspond au nom de catalogue que l’utilisateur sélectionne. Il permet de filtrer la liste des articles retournés pour n’afficher que ceux qui font partie du catalogue sélectionné.

JRXmlExporter XML

JROdtExporter ODT

- 19 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 328: Ejb Jsf Struts Flex Jasper

Créez le répertoire rapports dans le répertoire VenteEnLigneWeb\WebContent et ajoutez la JSP ListeDesArticlesJasper.jsp :

<<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Interface de reporting</title> </head> <body bgcolor="#FFFFB9"> <center> <form action="ListeArticlesJasperServlet"> <h1>Bienvenue sur l’interface de reporting </h1> <br></br> Sélectionnez le nom du catalogue : <select name="leCatalogue"> <option value="Sport">Sport</option> <option value="Gateaux">Gateaux</option> <option value="Informatique">Informatique</option> <option value="Bricolage">Bricolage</option> </select> <br></br> Sélectionnez le format du rapport : <select name="typeRapport"> <option value="pageHTML">HTML</option> <option value="pdf">PDF</option> <option value="xls">Excel</option> <option value="rtf">RTF</option> <option value="word2007">Word 2007</option> </select> <br></br> <input type="submit" value="Générer le rapport" /> </form> </center> </body> </html>

L’interface de reporting correspond à l’écran suivant :

- 20 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 329: Ejb Jsf Struts Flex Jasper

Pour la génération d’un rapport au format Excel, vous devez ajouter les librairies jxl­2.6.jar et poi­3.5­FINAL­20090928.jar dans le répertoire WEB­INF/lib. Le fichier Excel produit à partir de la requête EJBQL paramétrée et JasperReports, pour le catalogue Sport, est alors le suivant :

4. Utilisation de JasperReports dans Struts 2

L’intégration de JasperReports dans le framework Struts 2 est facilitée par un plugin qui fait partie de la distribution du framework. Il permet aux actions de retourner des rapports et correspond à la librairie struts2­jasperreports­plugin­2.1.8.jar qui se trouve dans le répertoire struts­2.1.8\lib\. Ce plugin et les librairies suivantes doivent être copiés vers le répertoire jboss­6.0.0.M1\server\default\lib :

jasperreports­3.6.1.jar

groovy­all­1.5.5.jar

iText­2.1.0.jar

- 21 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 330: Ejb Jsf Struts Flex Jasper

Depuis la page d’affichage des détails d’un article, il est souhaitable d’insérer un lien qui permet au client d’exporter la fiche des détails d’un article dans un fichier au format PDF.

Ce fichier correspond à un rapport généré de façon dynamique. Dans le projet VenteEnLigneWebStruts2, il faut modifier le fichier de configuration magasin.xml pour y définir un type de résultat Jasper :

<result-types> <result-type name="jasper" class="org.apache.struts2.views.jasperreports.JasperReportsResult"/> </result-types>

L’action com.eni.dvtejb.clientStruts2.action.JasperArticleAction utilise ce type de résultat, en cas de succès :

<<action name="afficheFichePDF" class="com.eni.dvtejb.clientStruts2.action.JasperArticleAction"> <result name="success" type="jasper"> <param name="location">rapports\ficheArticle.jasper</param> <param name="dataSource">artDetails</param> <param name="format">PDF</param> <param name="reportParameters">paramsRapport</param> </result> </action>

Ce type de résultat propose plusieurs paramètres dont certains sont utilisés dans cet exemple :

location : indique le chemin relatif, par rapport à l’URL courante, où le fichier compilé .jasper se trouve.

dataSource : la source de données. Cela peut être une collection de type List ou un entity bean (artDetails est une instance de l’entity bean Article).

format : le format du rapport à générer. PDF est le format par défaut.

reportParameters : correspond à une map contenant des paramètres et qui peut être récupérée depuis la value stack.

Seul le paramètre dataSource est obligatoire.

Le nom du rapport généré peut être spécifié à l’aide du paramètre documentName. La liste complète des paramètres est disponible sur le site officiel du plugin à l’adresse suivante :

http://struts.apache.org/2.x/docs/jasperreports­plugin.html

- 22 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 331: Ejb Jsf Struts Flex Jasper

Le code de l’action JasperArticleAction.java chargée de compiler et remplir les données du rapport est le suivant :

package com.eni.dvtejb.clientStruts2.action; import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.util.HashMap; import javax.swing.ImageIcon; import net.sf.jasperreports.engine.JasperCompileManager; import org.apache.struts2.ServletActionContext; import org.jboss.logging.Logger; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.services.ServiceLocator; import com.eni.dvtejb.metier.sessions.PanierBeanRemote; import com.opensymphony.xwork2.ActionSupport; public class JasperArticleAction extends ActionSupport private static final long serialVersionUID = 1L; // La dataSource du rapport private Article artDetails; public Article getArtDetails() return artDetails; // Les paramètres du rapport

- 23 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 332: Ejb Jsf Struts Flex Jasper

private HashMap paramsRapport = new HashMap(); public HashMap getParamsRapport() return paramsRapport; public String execute() throws Exception String numeroId = (String)ServletActionContext.getRequest().getParameter("numeroId"); long l = Long.parseLong(numeroId); PanierBeanRemote panierBeanRemote = (PanierBeanRemote) ServiceLocator.getInstance().getService("VenteEnLigne/PanierBean/remote"); artDetails = panierBeanRemote.findById(l); Image imageJasper = Toolkit.getDefaultToolkit().createImage(artDetails.getImage()); imageJasper = new ImageIcon(imageJasper).getImage(); BufferedImage bufferedImage = new BufferedImage(imageJasper.getWidth(null),imageJasper.getHeight(null),Buffe redImage.TYPE_INT_RGB ); paramsRapport.put("TitreRapport", "Fiche Article"); paramsRapport.put("SummaryImage", imageJasper); try JasperCompileManager.compileReportToFile( ServletActionContext.getServletContext().getRealPath("/rapports/ficheArtic le.jrxml"), ServletActionContext.getServletContext().getRealPath("/rapports/ficheArtic le.jasper")); catch (Exception e) e.printStackTrace(); return ERROR; return SUCCESS;

La Hashmap paramsRapport contient les paramètres TitreRapport et SummaryImage qui correspondent respectivement au titre du rapport et à l’image de l’article.

Ces paramètres sont définis dans le fichier VenteEnLigneWebStruts2\WebContent\rapports\ficheArticle.jrxml créé dans iReport. Le fichier .JRXML contient les lignes suivantes :

<parameter name="TitreRapport" class="java.lang.String"/> <parameter name="SummaryImage" class="java.awt.Image"/> ... <image hAlign="Center" isUsingCache="false"> <reportElement x="186" y="6" width="82" height="41"/> <imageExpression class="java.awt.Image"> <![CDATA[$PSummaryImage]]> </imageExpression> </image>

Dans la JSP visualiser.jsp des détails d’un article, le lien suivant est ajouté pour appeler l’action JasperArticleAction :

<s:url id="ficheJasper" action="afficheFichePDF" > <s:param name="numeroId"><s:property value="#session.monArticle.articleid" /></s:param> </s:url> <s:a href="%ficheJasper">Export de la fiche article au format PDF</s:a>

L’id de l’article est passé en paramètre et est utilisé dans le stateful session bean PanierBean pour récupérer les détails de l’article. La fiche au format PDF s’affiche dans le browser et peut être sauvegardée :

- 24 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 333: Ejb Jsf Struts Flex Jasper

5. Tableau croisé dynamique avec JavaServer Faces 2

L’intégration de JasperReports dans JSF 2 peut se faire dans un managed bean en charge de générer le rapport.

Le rapport suivant génère un tableau croisé dynamique qui représente le nombre d’articles par catalogue et par produit. Ces données offrent plus de visibilité sur la répartition des articles disponibles et peuvent être importantes pour optimiser les ventes par exemple.

Un tableau croisé (en anglais « pivot table » ou « cross tab ») est une transformation de lignes de données en colonnes. C’est donc un tableau à deux dimensions. Ces données sont habituellement agrégées (total, somme, moyenne, minimum, etc.).

Il est dynamique car les noms (et leurs nombres) des colonnes et des lignes ne sont pas définis dans la template.

Pour créer des tableaux croisés dynamiques, JasperReports utilise la balise <crosstab>.

L’outil iReport propose cependant un wizard très pratique pour la génération de ce type de tableau.

Créez un fichier tableauCroise.jrxml dans le répertoire WebContent\rapports du projet VenteEnLigneWebJSF2 et insérez la requête suivante :

<queryString language="ejbql"> <![CDATA[select a.nom, c.nom, p.nom, s.quantite from Article a, Catalogue c, Produit p, Stock s where a.produitFk = p.produitid and p.catalogueFk = c.catalogueid and a.stockFK = s.stockid group by a.nom, c.nom, p.nom, s.quantite]]> </queryString> <field name="COLUMN_1" class="java.lang.String"/> <field name="COLUMN_2" class="java.lang.String"/> <field name="COLUMN_3" class="java.lang.String"/> <field name="COLUMN_4" class="java.math.BigDecimal"/>

Seulement deux sections sont nécessaires dans cette template : Title et Summary. Les tailles des autres sections peuvent donc être réduites à 0. Le tableau croisé dynamique est placé dans la section Summary du template.

- 25 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 334: Ejb Jsf Struts Flex Jasper

Dans la palette, faites un glisser­déposer de l’élément crosstab vers la zone Summary du template :

Un wizard s’affiche alors pour aider dans la création du tableau croisé dynamique. Choisissez Main Report Dataset dans le premier écran et cliquez sur Suivant :

- 26 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 335: Ejb Jsf Struts Flex Jasper

Dans l’écran suivant, le wizard offre la possibilité de sélectionner les groupes de données qui vont apparaître dans la colonne de gauche du tableau. Sélectionnez COLUMN_1, c’est­à­dire les noms des articles :

L’écran suivant propose de sélectionner les noms des champs qui seront affichés, par groupe, dans les titres des colonnes. Choisissez COLUMN_2 (catalogue) et COLUMN_3 (produit) :

- 27 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 336: Ejb Jsf Struts Flex Jasper

L’écran suivant demande de sélectionner la mesure. Une mesure est une valeur numérique qui décrit quantitativement les données du tableau. Sélectionnez COLUMN_4 (quantite) et la fonction Count pour calculer les quantités totales, par ligne et par colonne :

Enfin le dernier écran permet de définir le layout du tableau.

- 28 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 337: Ejb Jsf Struts Flex Jasper

Cochez Add row group totals, Add column group totals pour montrer le total de chaque ligne et le total de chaque colonne. Cochez également Show grid lines (adding cell border). Puis cliquez sur le bouton Terminer.

Le tableau croisé est ajouté dans le template. Il ne reste plus qu’à ajouter un titre dans la section Title et à compiler le rapport en cliquant sur le bouton Compile report.

Si la compilation se passe bien, un fichier tableauCroise.jasper est généré dans le même répertoire que celui où se trouve le fichier .jrxml c’est­à­dire dans VenteEnLigneWebJSF2\WebContent\rapports :

- 29 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 338: Ejb Jsf Struts Flex Jasper

Ce rapport peut être ajouté dans la partie Administration du projet VenteEnLigneWebJSF2. Créez la facelet reporting.xhtml suivante :

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <ui:composition xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/templateAdmin.xhtml"> <ui:define name="titre"> <h:outputText value=" Interface de reporting"/> </ui:define> <ui:define name="contenu"> <center> <f:view locale="#loginBean.locale"> <f:loadBundle basename="com.eni.dvtejb.clientJSF2.messages" var="lesMessages"/> <h:form id="FormReporting"> ... <h2><h:outputText value=" Interface de reporting"/> </h2> <h4> <h:commandLink value=" Générer le tableau croisé dynamique" action="generationRapport" actionListener="#rapportBean.genererRapport" /></h4> </h:form> </f:view> </center> </ui:define> </ui:composition>

La balise <h:commandLink> crée le lien qui active la méthode qui est à l’écoute de cette activation. Le nom de la méthode listener est défini dans l’attribut actionListener. Ce nom de méthode est préfixé par le nom du backing bean. C’est une méthode publique qui possède un paramètre javax.faces.event.ActionEventParameter et un type de retour void.

L’interface de reporting est insérée dans la partie Administration de l’application :

- 30 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 339: Ejb Jsf Struts Flex Jasper

Voici le code du backing bean com.eni.dvtejb.clientJSF2.beans.RapportBean.java :

package com.eni.dvtejb.clientJSF2.beans; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRExporterParameter; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.export.JRPdfExporter; import net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory; import net.sf.jasperreports.j2ee.servlets.ImageServlet; import org.apache.log4j.Logger; @ManagedBean @SessionScoped public class RapportBean private static final Logger log = Logger.getLogger(RapportBean.class);

- 31 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 340: Ejb Jsf Struts Flex Jasper

public void genererRapport(ActionEvent actionEvent) throws ClassNotFoundException, SQLException, IOException, JRException FacesContext facesContext = FacesContext.getCurrentInstance(); HttpServletResponse response = (HttpServletResponse)facesContext.getExternalContext().getResponse(); HttpServletRequest request = (HttpServletRequest)facesContext.getExternalContext().getRequest(); JasperPrint jasperPrint = null; InputStream reportStream = facesContext.getExternalContext().getResourceAsStream("/rapports/tab leauCroise.jasper"); ServletOutputStream servletOutputStream = response.getOutputStream(); facesContext.responseComplete(); // création de l’entity manager factory pour la connexion avec la base de données EntityManagerFactory emf = Persistence.createEntityManagerFactory("VenteEnLigneModuleEJB", new HashMap()); EntityManager em = emf.createEntityManager(); try log.info("------------------------- Debut remplissage du fichier et generation du fichier .jrprint"); Map parametresMap = new HashMap(); parametresMap.put(JRJpaQueryExecuterFactory.PARAMETER_JPA_ENTITY_MAN AGER, em); jasperPrint = JasperFillManager.fillReport(reportStream, parametresMap); log.info("-------------------------Fin remplissage du fichier et generation du fichier .jrprint"); catch (JRException e) e.printStackTrace(); finally if (em.isOpen()) em.close(); if (emf.isOpen()) emf.close(); try log.info("------------------------- Debut export PDF -------------------------"); JRPdfExporter exporterPDF = new JRPdfExporter(); request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_ SESSION_ATTRIBUTE, jasperPrint); exporterPDF.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); jasperPrint.getName(); exporterPDF.setParameter(JRExporterParameter.OUTPUT_STREAM, servletOutputStream); response.setContentType("application/pdf"); response.setHeader("Content-disposition", "filename=" + "tableauCroise.pdf;"); exporterPDF.exportReport(); log.info("------------------------- Fin export PDF------------------------- ");

- 32 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 341: Ejb Jsf Struts Flex Jasper

catch (JRException e) log.info("------------------------- JRException dans export PDF ------------------------- "); // affichage de la stack trace dans le browser StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); e.printStackTrace(printWriter); response.setContentType("text/plain"); servletOutputStream.print(stringWriter.toString()); e.printStackTrace(); servletOutputStream.flush(); servletOutputStream.close();

La phase de compilation du rapport n’intervient pas dans le backing bean puisque le rapport a déjà été compilé par iReport. Des objets de type HttpServletResponse et HttpServletRequest qui font partie de l’API des Servlets, obtenus grâce à la classe javax.faces.context.FacesContext, permettent de facilement créer un objet de type JasperPrint rempli avec les données de la requête EJBQL et de produire le rapport au format PDF.

Enfin, voici le tableau croisé dynamique qui s’affiche au format PDF, en plein écran pour plus de lisibilité, dans le navigateur Chrome :

- 33 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MsoHDIlmH82ICwA=-enidentnumber

Page 342: Ejb Jsf Struts Flex Jasper

Introduction à Flex

Les sites Web 2.0 sont des sites qui se caractérisent par leur interactivité et les échanges d’informations qu’ils fournissent entre utilisateurs. La conception de ces sites a été facilité grâce à l’apparition de solutions de développement telles que Flex d’Adobe, JavaFX d’Oracle/Sun ou de SilverLight de Microsoft, pour permettre de créer des applications internet riches (RIA).

Flex est un environnement qui consiste en un kit de développement et des composants d’interfaces utilisateur pour développer des applications internet riches ou pour le desktop (RDA) avec Flash et qui peuvent être exécutées dans le navigateur (avec Adobe Flash Player) ou sur le bureau (avec Adobe AIR). Le runtime Flash Player est installé sur une très large majorité de navigateurs dans le monde, ce qui fournit un avantage certain aux applications RIA développées avec Flex.

Flex permet donc de mettre la puissance de Flash dans les mains des développeurs Web pour pouvoir réaliser des interfaces séduisantes.

Cependant la réalisation d’interfaces graphiques séduisantes n’est plus réservée aux graphistes seulement. Grâce à Flex, elle est maintenant à la portée des développeurs Java également. Ce chapitre met en place une interface Web avec Flex dans une application Java EE et montre l’utilisation des EJB dans certaines fonctionnalités, grâce à GraniteDS.

Le but de ce sous­chapitre est de fournir une introduction à des notions basiques dans Flex, notamment sur celles qui sont utilisées pour le développement des fonctionnalités abordées du client.

1. Flex SDK et Flex Builder

Lorsqu’on parle de Flex, il faut distinguer le kit de développement Flex SDK (gratuit) de l’environnement de développement Flex Builder (payant) qui est basé sur Eclipse. Flex SDK repose sur des outils qui s’exécutent en ligne de commande, tel que MXMLC qui sert à compiler les fichiers MXML et ActionScript en fichier .swf. Pour développer une application Flex, il est recommandé, pour des raisons de productivité évidentes, d’utiliser Flex Builder qui fournit, entre autres, de la complétion de code, la coloration syntaxique, un profiler, un débogueur et un mode design. Ce mode design est une représentation graphique de l’application en cours de construction et permet de faire un glisser­déposer (drag and drop) de composants Flex.

Flex dans sa version 1.0 est né en 2004 (Macromedia). La version 2.0 est apparue en 2006 (Adobe) et la version 3.0 en 2008. Au moment de l’écriture de ce chapitre, Flex 4.0 est encore en version béta. À partir de cette version, l’outil Flex Builder (payant) est renommé Flash Builder afin d’éviter la confusion avec le framework Flex (Open Source).

AMF3 (ActionScript Message Format) est le format binaire utilisé pour l’échange d’objets entre le client et le serveur. AMF encode les appels distants / RPC (Remote Procedure Call) dans un format binaire qui peut être transféré via le protocole HTTP ou HTTPS. Les objets et données sont sérialisés dans ce format binaire, ce qui augmente la performance car la sérialisation/désérialisation AMF est une procédure très optimisée dans Flash Player.

L’API Flex est consultable sur le site d’Adobe à l’adresse suivante : http://www.adobe.com/livedocs/flex/

Les socles d’exécution des applications Flex dépendent du type :

Flash Player sur le système d’exploitation pour les applications de type RDA (AIR).

Flash Player dans le navigateur pour les applications de type RIA (FLEX).

Les applications Flex sont exécutées via le plugin Flash Player pour le navigateur. Il faut écrire le code et le compiler avec le compilateur Flex pour générer un fichier .swf (Shockwave Flash, se prononce « swiff ») qui est chargé et exécuté dans Flash Player. Le fichier .swf correspond à du bytecode Flash et est habituellement référencé à l’intérieur d’un fichier HTML. Les principaux éléments du code dans Flex sont :

MXML (Macromedia Flex Markup Language) : c’est un langage déclaratif basé sur XML qui permet d’écrire en quelques lignes une interface riche.

ActionScript : le langage utilisé pour définir les actions entre les composants.

Un fichier contenant du MXML possède l’extension .mxml. Les tags MXML permettent de définir des contrôles UI (User Interface) Flex tels que <mx:dataGrid>, <mx:Button> et <mx:ComboBox>. Ils permettent également de définir des composants qui ne sont pas de type UI tels que <mx:NumberFormatter> pour formatter des données et <mx:HTTPService> pour envoyer et recevoir des données.

Des propriétés sont utilisées pour définir les attributs des composants. Ces propriétés peuvent correspondre, entre autres, à des évènements (clic sur un bouton) ou des effets. Le langage MXML 2006 correspond à la version 3 de Flex alors que le langage MXML 2009 correspond à la version 4.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 343: Ejb Jsf Struts Flex Jasper

Macromedia est l’entreprise qui a créé Flex et qui a été acquise par Adobe en 2005.

La version standalone de Flex Builder est un outil de développement pour Flex, basé sur Eclipse. La version plugin pour Eclipse est disponible (version d’essai de 60 jours) à l’adresse suivante :

https://www.adobe.com/products/flex/features/flex_builder/

Cet outil est renommé Flash Builder dans la prochaine version de Flex, la version 4. Reportez­vous au chapitre Mise en place de l’environnement pour l’installation de la version plugin dans Eclipse.

Flex Builder en mode design :

Les applications RIA développées avec Flex nécessitent d’avoir installé le Flash Player dans le navigateur. Le player est disponible à l’URL suivante : http://www.adobe.com/support/flashplayer/downloads.html . Les

applications RDA développées avec Flex nécessitent d’installer le moteur d’exécution Adobe AIR (environ 15 MB) sur le poste.

2. Les conteneurs

Les conteneurs sont des composants dans lesquels d’autres composants peuvent être insérés. Ils ont des layouts (mises en page) prédéfinis.

Les types de layouts sont :

absolute : les composants à l’intérieur des conteneurs sont placés suivant des coordonnées (x,y).

vertical : les composants sont placés de façon verticale et centrée.

horizontal : les composants sont placés de façon horizontale, de la gauche vers la droite.

Voici une liste non exhaustive de conteneurs disponibles dans Flex 3 :

Installation du plugin Flex Builder

Tag MXML Remarques

<mx:Accordion> Ce conteneur de navigation contient des conteneurs enfants dont un seul est visible à la fois.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 344: Ejb Jsf Struts Flex Jasper

Une application Flex est constituée d’une hiérarchie de composants dont le composant racine est Application. Le layout par défaut des composants est vertical.

Pour de plus amples détails sur les composants disponibles dans Flex, Adobe a créé une application nommée Tour de Flex et accessible à l’URL suivante : http://www.adobe.com/devnet/flex/tourdeflex/web/

3. Les contrôles

Les contrôles sont des composants qui, de manière générale, servent à afficher des données ou à ajouter de l’interactivité. On peut distinguer plusieurs catégories de contrôles :

les contrôles textuels qui permettent d’afficher ou de collecter des données (texte, date, image, vidéo…) :

<mx:text>, <mx:label>, <mx:TextInput>, <mx:TextArea>, <mx:RichTextEditor>, <mx:NumericStepper>, <mx:DateField>, <mx:ColorPicker>, <mx:DateChooser>, <mx:Image>, <mx:VideoDisplay>, <mx:Datagrid>, <mx:AdvancedDatagrid>

les contrôles de type bouton :

<mx:Button>, <mx:LinkButton>, <mx:CheckBox>, <mx:RadioButton>, <mx:PopupButton>, <mx:ComboBox>

les contrôles interactifs :

<mx:HSlider>, <mx:VSlider>, <mx:HScrollBar>, <mx:VScrollBar>

<mx:ApplicationControlBar> Contient d’autres conteneurs pour des commandes de navigation et d’application.

<mx:Canvas> Les positions des enfants sont spécifiées avec les coordonnées (x,y).

<mx:ControlBar> Ce conteneur permet de regrouper des composants, à l’intérieur d’un composant de type Panel ou TiteWindow.

<mx:Form> Permet d’afficher les contrôles d’entrée avec leur label.

<mx:FormHeading> Spécifie un label pour un groupe de conteneurs FormItem.

<mx:Grid> Une grille. L’équivalent du tag <TABLE> dans HTML.

<mx:HBox> Conteneur qui aligne ses composants de manière horizontale.

<mx:HDividedBox> Les composants sont alignés horizontalement et un séparateur déplace ces composants vers la gauche ou vers la droite.

<mx:HRule> / <mx:VRule> Ligne horizontale/verticale pour diviser le contenu d’un conteneur.

<mx:Panel> Un conteneur qui contient d’autres conteneurs. Il a un titre, un message de status, une bordure et une zone de contenu.

<mx:Spacer> Composant invisible qui aide à disposer les composants en remplissant l’espace.

<mx:Tile> Une grille dont les cellules sont alignées de façon horizontale ou verticale.

<mx:VBox> Conteneur qui aligne ses composants de manière verticale.

<mx:VDividedBox> Les composants sont alignés verticalement et un séparateur déplace ces composants vers le haut ou vers le bas.

<mx:ViewStack> Une collection de conteneurs, empilés les uns sur les autres et dont un seul est visible à la fois.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 345: Ejb Jsf Struts Flex Jasper

4. Les évènements

Dans Flex, les interactions entre les composants peuvent être gérées à l’aide d’évènements. Il y a deux principaux types d’évènements : les évènements qui permettent de récupérer les interactions de l’utilisateur et les évènements systèmes comme l’instanciation d’un composant.

Par exemple, l’évènement clic sur un bouton est un évènement de type utilisateur et peut être géré à l’intérieur du tag <mx:Button> avec la propriété click :

<mx:Button id="panierBouton" label="Ajouter au panier" click="ajouterArticleAuPanier()"/>

Ainsi, lorsque l’utilisateur clique sur le bouton, la méthode ajouterArticleAuPanier() est déclenchée.

Un exemple d’évènement système est le déclenchement de l’évènement CreationComplete :

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="450" creationComplete="srv.findBoutique()">

Cet évènement est déclenché quand le composant Canvas a été initialisé.

Des évènements personnalisés peuvent également être créés en héritant de la classe flash.events.Event. C’est le cas de la méthode connexion() qui est mise en place dans l’écran d’authentification à l’application et qui est détaillée plus loin dans ce chapitre.

5. La navigation

Le composant ViewStack représente une pile de vues empilées les unes sur les autres. Une seule vue est visible à la fois. Ces vues sont des conteneurs. Dans le layout de l’application, le composant ToggleButtonBar est utilisé pour aider à la navigation en sélectionnant la vue à afficher. Il garde le bouton enfoncé même si l’utilisateur le relâche.

Extrait du fichier ecranPrincipal.mxml :

<mx:VBox width="188" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" > <mx:Label text="MENU" /> <mx: ToggleButtonBar id="buttonBar" direction="vertical" dataProvider="maViewStack" themeColor="haloOrange" fontSize="16" width="186" height="439" /> </mx:VBox> <mx:ViewStack id="maViewStack" width="100%" height="100%" backgroundAlpha="0.0"> <mx:VBox id="conteneur1" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Accueil" > <comps:ecranAccueil width="100%" /> </mx:VBox> <mx:VBox id="conteneur2" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Mon profil"> <comps:profil width="100%" /> </mx:VBox> ...

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 346: Ejb Jsf Struts Flex Jasper

<mx:VBox id="conteneur7" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Deconnexion" click="logout()"> </mx:VBox> </mx:ViewStack>

Le composant Vbox, dont l’identifiant est conteneur1, est le premier composant de la pile, c’est donc celui qui est affiché par défaut. L’utilisateur connecté arrive donc directement sur la vue Accueil.

Dans Flex Builder, pour avoir des informations sur un composant, vous pouvez utiliser l’aide en sélectionnant un élément et en appuyant sur la touche F1. La première fois que vous invoquez l’aide, l’indexation peut

prendre plusieurs minutes.

6. Les états

Les états sont une collection de changements dans une vue.

Le tag <mx:states> contient des états définis dans des tags enfants <mx:state> et permet d’ajouter ou de supprimer des contrôles (composants, propriétés, styles, évènements) dans l’état initial.

Le tag <mx:setProperty> permet de changer les propriétés du composant. Le style est modifié avec le tag <mx:SetStyle> et les handlers d’évènements sont ajoutés avec <mx:SetEventHandler>.

Dans l’application, l’écran de connexion possède un lien « S’inscrire » pour permettre à un nouvel utilisateur de s’inscrire. Ce lien appelle un état qui permet de changer le contenu du formulaire :

<?xml version="1.0" encoding="utf-8"?> <mx:Form xmlns:mx="http://www.adobe.com/2006/mxml" width="266" height="550" > ... <mx:states> <mx:State name="Inscription" id="inscr"> <mx:SetProperty target="LoginForm" name="height" value="540" /> <mx:SetProperty target="Application.application.panelLogin" name="title" value="Rentrez vos informations" /> <mx:AddChild position="lastChild" creationPolicy="all"> <mx:FormItem id="nom" label="Nom:"> <mx:TextInput/> </mx:FormItem> </mx:AddChild> <mx:AddChild position="lastChild" creationPolicy="all"> <mx:FormItem id="prenom" label="Prenom:"> <mx:TextInput/> </mx:FormItem> </mx:AddChild> ... <mx:AddChild position="lastChild"> <mx:LinkButton label="Retour" click="currentState=’’"/> </mx:AddChild> <mx:RemoveChild target="leheading"/> <mx:RemoveChild target="loginBtn"/> <mx:RemoveChild target="lienInscription"/> </mx:State> </mx:states> <mx:transitions> <mx:Transition fromState="*" toState="Inscription"> <mx:Fade target="Application.application.panelLogin"/> </mx:Transition>

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 347: Ejb Jsf Struts Flex Jasper

</mx:transitions> <mx:FormHeading id="leheading" label="Formulaire de connexion" /> <mx:FormItem label="Login:" required="true" > <mx:TextInput id="userName" /> </mx:FormItem> <mx:FormItem label="Password:" required="true"> <mx:TextInput id="password" displayAsPassword="true" /> </mx:FormItem> <mx:FormItem width="237"> <mx:Button id="loginBtn" label="Login" icon="@Embed(source=’../images/login.png’)" enabled="(userName.text.length == 0 || password.text.length == 0) ? false : true" toolTip="loginBtn.enabled == true ? ’Cliquez pour soumettre’ : ’Entrez le login et le password’" click="processLogin()" width="112" height="45"/> </mx:FormItem> <mx:LinkButton id="lienInscription" label="S’inscrire" height="20" click="currentState=’Inscription’"/> </mx:Form>

L’écran change d’état pour afficher le formulaire d’inscription :

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 348: Ejb Jsf Struts Flex Jasper

Les changements d’états peuvent être accompagnés d’effets de transition avec le composant <mx:Transition>. Par défaut, le changement d’états (passage du formulaire de login au formulaire d’inscription) se fait visuellement de façon abrupte. L’ajout d’une transition peut par exemple ralentir ce changement d’états pour produire un effet visuel plus agréable. Ainsi le tag <mx:Fade> a pour effet de diminuer progressivement la transparence du formulaire d’inscription.

Flex fournit également en standard un moyen de valider les formulaires de saisie : les Validators. C’est un ensemble de classes ActionScript qui se trouvent dans le package mx.validators et qui héritent de la classe mx.validators.Validator. Par exemple : CreditCardValidator pour valider un numéro de carte de crédit, EmailValidator pour valider une adresse email, ZipCodeValidator pour valider un code postal selon un pattern.

Le caractère obligatoire des champs est spécifié dans la propriété required avec la valeur true.

7. Le binding de données

Le binding de données, ou liaison de données, est un concept très utilisé dans Flex. Il permet de lier un composant A à un autre composant B. Le changement de valeur du composant A (la source) est automatiquement détecté et met à jour la valeur du composant B (la destination).

Une façon de définir un binding de données consiste à utiliser les accolades . Dans l’exemple suivant, les propriétés Text des composants de type Label sont liés au composant de type TextInput. Les expressions sont entourées d’accolades et pointent vers l’identifiant (id champ1) du composant de type TextInput :

<?xml version="1.0" encoding="utf-8"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300"> <mx:TextInput id="champ1" text=""/> <mx:Label id="Label1" text="Nombre de caractères: champ1.text.length"/> <mx:Label id="Label2" text="Contenu de champ1: champ1.text"/> </mx:Panel>

Lorsque l’utilisateur entre une suite de caractères, celle­ci est immédiatement affichée dans le label dont l’identifiant est Label2 et le nombre de caractères est affiché dans le label dont l’identifiant est Label1.

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 349: Ejb Jsf Struts Flex Jasper

Une autre façon de définir un binding de données passe par l’utilisation de l’annotation [Bindable] dans du code ActionScript. Dans l’exemple suivant, la variable date, annotée avec l’annotation [Bindable], est la source de données du composant Label :

<mx:Script> <![CDATA[ [Bindable] public var date:Date = new Date(); ... ]]> </mx:Script> <mx:DateFormatter id="formatDate" formatString="DD/MM/YYYY" /> ... <mx:Label text="Nous sommes le : formatDate.format(date)" textAlign="center"/>

Un des intérêts du data binding est de pouvoir réutiliser dans des vues de l’application des variables définies et mises à jour dans une autre vue.

8. Le langage ActionScript 3.0

ActionScript 3.0 est un langage orienté objet qui à l’origine a été développé pour permettre aux développeurs de programmer de façon interactive dans des applications Flash. Dans Flex, il est utilisé pour ajouter de l’interactivité avec MXML.

Le code ActionScript peut être écrit dans des classes (à l’intérieur de fichiers qui ont l’extension .as) ou bien embarqué dans les fichiers MXML. Chaque tag MXML correspond à une classe ActionScript. Par exemple le tag <mx:Label> correspond à la classe Label du package mx.controls.

Voici donc un aperçu des bases du langage qu’il est utile de connaître :

les variables sont déclarées avec le mot clé var ;

le langage est sensible à la casse ;

la visibilité par défaut des classes est internal (la visibilité est limitée aux classes du même package) ;

le mot clé as peut être utilisé pour caster.

Le type d’une variable est défini de la façon suivante :

var maVariable:type;

Les types primitifs supportés par le langage sont :

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 350: Ejb Jsf Struts Flex Jasper

String : une chaîne de caractères ;

Boolean : un booléen dont la valeur est true ou false ;

Numeric : une valeur numérique qui peut être de type entier ou décimal ;

int : une valeur numérique de type entier ;

uint (unsigned int) : une valeur numérique de type entier et positive.

ActionScript peut être utilisé à l’intérieur des fichiers MXML à l’aide du tag <mx:script> :

<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ ... ]]> </mx:Script> ... </mx:Application>

CDATA indique que le code ActionScript ne doit pas être parsé par le parseur XML.

La directive include peut également être utilisée :

<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ ... include "/com/eni/dvtejb/metier/dtos/MagasinDTO.as"; ]]> </mx:Script> ... </mx:Application>

À l’intérieur d’un composant MXML, ActionScript est souvent utilisé pour définir des méthodes qui deviennent alors des méthodes du composant. Une méthode (ou fonction) est définie avec le mot­clé function comme dans l’exemple suivant :

private function additionner(nombre1:uint, nombre1:uint):uint var total:uint = nombre1 + nombre2; return total;

Le type de retour de la méthode additionner est uint. Le mot­clé void est utilisé pour spécifier qu’une méthode ne retourne rien.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mp1kpOpmH82ICwA=-enidentnumber

Page 351: Ejb Jsf Struts Flex Jasper

Intégration des EJB 3 avec GraniteDS

1. Présentation de Granite Data Services

Pour faire communiquer une application Flex avec un serveur et faire des appels RPC, l’API Flex dispose de plusieurs composants :

<mx:HTTPService> qui permet de se connecter à une URL et charger du code XML dans Flash Player.

<mx:WebService> pour interagir avec des Web Services. La classe WebService envoie et reçoit des messages SOAP à travers HTTP.

<mx:RemoteObject> pour interagir avec des objets Java à travers le format AMF3.

Ces composants ont leurs avantages et leurs inconvénients. En ce qui concerne l’appel des EJB, il existe diverses solutions parmi lesquelles GraniteDS, WebORB, LCDS et BlazeDS.

GraniteDS est une implémentation Open Source et côté serveur du format AMF. Le format AMF (Action Message Format) permet le transfert de données binaires entre les objets côté serveur (EJB, classes Java, etc) et le client (application Web Flex).

GraniteDS représente une alternative gratuite et Open Source à LCDS (LiveCycle Data Services) qui est la version commerciale de BlazeDS.

Les raisons qui justifient l’utilisation de GraniteDS avec un backend Java / EJB, au lieu de BlazeDS ou LCDS, qui sont les deux solutions officielles fournies par Adobe, sont diverses :

BlazeDS, une version Lite de LCDS, est essentiellement une librairie de remoting et de messaging. Elle ne fournit pas pour le moment des services de gestion de données avancés. Ceux­ci sont fournis dans la version commerciale LCDS. Il n’y a pas de support intégré du lazy­loading.

LCDS offre des fonctionnalités de gestion de données très avancées, mais qui nécessitent de suivre la philosophie du produit, à savoir que l’application est implémentée principalement du côté du client en Flex à la fois pour la partie présentation et pour la logique métier, le serveur LCDS servant plus ou moins de moteur de persistance distant. Il est possible d’utiliser LCDS avec des EJB, mais dans ce cas on perd la plupart des fonctionnalités avancées et on se retrouve avec un BlazeDS un peu amélioré.

GraniteDS au contraire a été développé spécifiquement pour intégrer des applications serveurs Java EE avec des clients Flex pour permettre à un client Flex d’appeler des EJB exactement comme un client Java, en utilisant notamment des entités JPA détachées c’est­à­dire qui ne sont plus associées à un contexte de persistence et ne sont donc plus managées par l’entity manager. Le framework Tide, qui fait partie du projet GraniteDS, permet d’avoir une intégration encore plus poussée en ce qui concerne la sécurité et la validation des données. GraniteDS supporte les sessions Hibernate et Java EE 6. Concernant la sérialisation, les externalizers permettent de sérialiser et désérialiser les propriétés d’un entity bean, qu’elles soient privées ou non.

Le lazy loading, c’est­à­dire le chargement des données uniquement quand elles sont demandées par l’application, est un facteur important pour améliorer les performances (temps et tailles des réponses).

Les développeurs principaux de GraniteDS, Franck Wolff et William Draï, maintiennent un blog disponible à l’adresse suivante : http://graniteds.blogspot.com

2. Création du projet Flex

L’installation du plugin Flex Builder dans Eclipse est décrite dans le chapitre Mise en place de l’environnement. La création et la configuration d’un projet Flex qui utilise GraniteDS sont détaillées ci­après.

1 ­ Commencez par créer un projet Web dynamique et nommez­le VenteEnLigneFlexGranite :

Pourquoi choisir GraniteDS ?

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 352: Ejb Jsf Struts Flex Jasper

Cliquez sur Finish.

La structure du fichier est maintenant la suivante :

Lorsque le projet est créé, ajoutez­le dans le working set pour qu’il apparaisse dans l’explorateur de packages.

2 ­ L’installation du plugin Flex Builder permet d’ajouter la nature Flex à un projet.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 353: Ejb Jsf Struts Flex Jasper

Faites un clic droit sur le projet et sélectionnez Add Flex project nature.

Choisissez J2EE comme type de serveur d’application. Laissez la case Use remote object access service vide et cliquez sur Next.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 354: Ejb Jsf Struts Flex Jasper

Cliquez sur Finish.

La structure du projet VenteEnLigneFlexGranite est alors la suivante :

Comme le montre l’écran, suite à l’ajout de la nature Flex, une croix rouge peut apparaître au niveau du titre du projet pour signaler l’erreur suivante dans la vue Problems d’Eclipse :

Cannot create HTML wrapper. Right­click here to recreate folder html­template. La solution préconisée est alors de faire un clic droit pour recréer le répertoire. S’il n’existe pas, créez directement un projet Flex temporaire depuis la perspective Flex et faites un copier­coller du répertoire html­template vers le projet VenteEnLigneFlexGranite. Le répertoire bin­debug est immédiatement mis à jour suite à l’ajout du répertoire html­template.

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 355: Ejb Jsf Struts Flex Jasper

Enfin, il est nécessaire de modifier le fichier .flexProperties qui indique l’URL du serveur et la racine de contexte du projet :

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <flexProperties flexServerType="8" serverContextRoot="/VenteEnLigneFlexGranite" serverRoot="" serverRootURL="http://localhost:8085/" toolCompile="true" useServerFlexSDK="false" version="1"/>

3. Génération des classes ActionScript 3.0 à l’aide de GAS 3

GraniteDS a une approche MDA (Model Driven Architecture), c’est­à­dire une approche guidée par les modèles et les outils. Dans le cas du plugin GAS3 (GraniteDS ActionScript 3), les modèles sont des templates Groovy (suffixe .gsp) de génération de classes ActionScript. Ces templates peuvent être personnalisées.

Il faut maintenant ajouter la nature GraniteDS au projet EJB, VenteEnLigneEJB. Suite à l’installation du plugin GraniteDS, l’option Add GraniteDS Nature est disponible lorsque l’on fait un clic droit sur le projet.

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 356: Ejb Jsf Struts Flex Jasper

Sélectionnez cette option. Un wizard apparaît alors. La première étape consiste à inclure les répertoires contenant les classes Java qui doivent être converties en classes ActionScript 3. Modifiez le répertoire de sortie de génération des classes AS3. Pour cela spécifiez dans le champ Output Directory le répertoire suivant : ../VenteEnLigneFlexGranite/flex_src/

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 357: Ejb Jsf Struts Flex Jasper

Les fichiers AS3 seront donc générés dans le projet VenteEnLigneFlexGranite, sous le répertoire /flex_src/.

La deuxième étape permet de sélectionner les projets Granite dépendants. Il n’y en a pas.

Cliquez donc sur Next.

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 358: Ejb Jsf Struts Flex Jasper

La troisième étape sert à sélectionner les librairies JARs et les répertoires des classes compilées qui font partie du classpath.

Utilisez le classpath du projet qui est indiqué par défaut et cliquez sur Next.

La quatrième étape permet de choisir les templates qui seront utilisées pour la génération des classes AS3.

Laissez les templates indiquées par défaut et cliquez sur Next.

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 359: Ejb Jsf Struts Flex Jasper

La dernière étape permet de modifier les options de génération des classes AS3.

Laissez les valeurs par défaut et cliquez sur Finish.

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 360: Ejb Jsf Struts Flex Jasper

Les classes AS3 sont alors automatiquement générées dans le répertoire /flex_src/ du projet VenteEnLigneFlexGranite. Le processus de génération apparaît dans la console :

Building project "VenteEnLigneEJB" (20/02/10 17:54)... [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtjeb\metier\interceptors\NouvelUtilisateurIn terceptor.as (output file does not exist) [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtjeb\metier\interceptors\NouvelUtilisateurIn terceptorBase.as (output file does not exist) ... [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtejb\metier\entities\Article.as (output file does not exist) [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtejb\metier\entities\ArticleBase.as (output file does not exist) [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtejb\metier\entities\Adresse.as (output file does not exist) [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtejb\metier\entities\AdresseBase.as (output file does not exist) [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtejb\metier\entities\Administrateur.as (output file does not exist) [INFO] Generating: K:\ENI\DeveloppementEJB3\workspaceGalileo\VenteEnLigneEJB\..\VenteEnLigneF lexGranite\flex_src\com\eni\dvtejb\metier\entities\AdministrateurBase.as (output file does not exist) Done (98 affected files - 28394ms).

À la fin du wizard, un fichier .granite est ajouté à la racine du projet VenteEnLigneEJB. Son contenu est le suivant :

<?xml version="1.0" encoding="UTF-8"?> <graniteProperties version="2.0"> <gas3 uid="uid" as3TypeFactory="org.granite.generator.as3.DefaultAs3TypeFactory" entityFactory="org.granite.generator.as3.DefaultEntityFactory" remoteDestinationFactory="org.granite.generator.as3.DefaultRemoteDestinati onFactory" debugEnabled="false" flexConfig="false"> <source path="ejbModule" includes="" excludes="" output="../VenteEnLigneFlexGranite/flex_src/;"/> <template kind="REMOTE_DESTINATION" uris="class:org/granite/generator/template/remote.gsp;class:org/granite/ge nerator/template/remoteBase.gsp"/> <template kind="BEAN" uris="class:org/granite/generator/template/bean.gsp;class:org/granite/gene rator/template/beanBase.gsp"/> <template kind="ENTITY" uris="class:org/granite/generator/template/entity.gsp;class:org/granite/ge nerator/template/entityBase.gsp"/> <template kind="ENUM" uris="class:org/granite/generator/template/enum.gsp"/> <template kind="INTERFACE" uris="class:org/granite/generator/template/interface.gsp"/> <transformer type="org.granite.generator.as3.JavaAs3GroovyTransformer"/> </gas3> </graniteProperties>

Pour supprimer la nature GraniteDS du projet, il faut utiliser le menu « Remove GraniteDS Nature », ce qui supprime la

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 361: Ejb Jsf Struts Flex Jasper

nature et la référence au builder granite dans le fichier .project d’Eclipse. Il n’est pas nécessaire de supprimer le fichier de configuration .granite. S’il existe, il sera réutilisé si l’on ajoute à nouveau la nature GraniteDS.

Par la suite, il est possible de modifier les paramètres de GAS3 dans les propriétés du projet :

À chaque fois qu’un EJB est modifié/créé, le générateur GAS3 est automatiquement lancé pour mettre à jour/créer les classes ActionScript.

Il est nécessaire de modifier les propriétés du projet.

Faites un clic droit sur le projet et cliquez sur Propriétés. Allez dans Flex Server et modifiez les champs Root URL et Context root avec les valeurs suivantes :

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 362: Ejb Jsf Struts Flex Jasper

L’arborescence du projet est constituée des répertoires suivants :

.settings : configuration spécifique à Eclipse.

bin­debug : contient les sources compilées. Ce répertoire est mis à jour à chaque build.

flex­libs : contient les fichiers SWC de l’application.

flex_src : contient les fichiers .MXML et .AS, générés ou non par GraniteDS.

html­template : les templates HTML.

src : peut contenir des sources Java et des fichiers de configuration.

WebContent : contient les ressources à publier. Le sous­répertoire WEB­INF/flex contient les fichiers de configuration de Flex et de GraniteDS.

Pour résumer, un projet nommé VenteEnLigneFlexGranite qui contient la nature Flex a été créé et la nature GraniteDS a été ajoutée au projet VenteEnLigneEJB, créé dans les précédents chapitres :

- 12 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 363: Ejb Jsf Struts Flex Jasper

4. Ajout des librairies et des fichiers de configuration

Les librairies GraniteDS doivent être ajoutées au projet VenteEnLigneFlexGranite. Copiez dans le répertoire WEB­INF/lib les librairies granite.jar et granite­hibernate.jar qui se trouvent dans le répertoire build de la distribution de GraniteDS.

Deux fichiers de configuration sont nécessaires : WEB­INF/flex/services­config.xml et WEB­INF/granite/granite­config.xml. Vous pouvez les récupérer depuis le répertoire graniteds/examples/graniteds_ejb3/resources/WEB­INF de la distribution, par exemple.

Le fichier services­config.xml contient les factories utilisées dans les destinations. Les destinations correspondent ici aux EJB que l’on souhaite exposer et invoquer. Voici un extrait du fichier services­config.xml :

<?xml version="1.0" encoding="UTF-8"?> <services-config> <services> <service id="granite-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage"> <adapters> <adapter-definition id="default" class="org.granite.gravity.adapters.SimpleServiceAdapter" default="true"/> </adapters> <destination id="destFacadeBean"> <channels> <channel ref="my-graniteamf"/> </channels> <properties> <factory>ejbFactoryFacadeBean</factory> </properties>

- 13 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 364: Ejb Jsf Struts Flex Jasper

</destination> <destination id="destPanierBean"> <channels> <channel ref="my-graniteamf"/> </channels> <properties> <factory>ejbFactoryPanierBean</factory> </properties> </destination> <destination id="destRechercheBean"> <channels> <channel ref="my-graniteamf"/> </channels> <properties> <factory>ejbFactoryRechercheBean</factory> </properties> </destination> </service> </services> <factories> <factory id="ejbFactoryFacadeBean" class="org.granite.messaging.service.EjbServiceFactory"> <properties> <lookup>VenteEnLigne/FacadeBean/remote</lookup> </properties> </factory> <factory id="ejbFactoryPanierBean" class="org.granite.messaging.service.EjbServiceFactory"> <properties> <lookup>VenteEnLigne/PanierBean/remote</lookup> </properties> </factory> <factory id="ejbFactoryRechercheBean" class="org.granite.messaging.service.EjbServiceFactory"> <properties> <lookup>VenteEnLigne/RechercheBean/remote</lookup> </properties> </factory> </factories> <security> <login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/> </security> <channels> <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://server.name:server.port/context.root/graniteamf/amf" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition> <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel"> <endpoint url="https://server.name:server.port/context.root/messagebroker/amfs ecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/> <properties> <add-no-cache-headers>false</add-no-cache-headers> </properties> </channel-definition> </channels> <system> <redeploy> <enabled>false</enabled>

- 14 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 365: Ejb Jsf Struts Flex Jasper

</redeploy> </system> </services-config>

Trois destinations sont définies : destFacadeBean, destPanierBean et destRechercheBean. Elles correspondent aux trois factories associées aux EJB FacadeBean, PanierBean et RechercheBean, respectivement, qui ont été développés dans les chapitres précédents. Ces factories sont utilisées dans les fonctionnalités du client décrites dans la suite de ce chapitre.

Dans le fichier granite­config.xml, il faut spécifier les externalizers à utiliser :

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE granite-config PUBLIC "-//Granite Data Services//DTD granite- config internal//EN" "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd"> <granite-config scan="true"> <!-- Use automatic configuration (scan="true" above). !--> <class-getter type="org.granite.hibernate.HibernateClassGetter"/> <externalizers> <externalizer type="org.granite.hibernate.HibernateExternalizer"> <include annotated-with="javax.persistence.Entity"/> <include annotated-with="javax.persistence.MappedSuperclass"/> <include annotated-with="javax.persistence.Embeddable"/> </externalizer> <externalizer type="org.granite.messaging.amf.io.util.externalizer.EnumExternalizer"> <include instance-of="java.lang.Enum"/> </externalizer> </externalizers> <!-- ! Use Tomcat [JBoss] based security service. !--> <security type="org.granite.messaging.service.security.TomcatSecurityService"/> </granite-config>

Le descripteur de déploiement WEB­INF/web.xml doit être modifié pour inclure, entre autres, la servlet AMFMessageServlet et le filtre AMFMessageFilter :

<!-- Granite config context listener --> <listener> <listener- class>org.granite.config.GraniteConfigListener</listener-class> </listener> <!-- gestion des requêtes AMF ([de]serialization) --> <filter> <filter-name>AMFMessageFilter</filter-name> <filter- class>org.granite.messaging.webapp.AMFMessageFilter</filter-class> </filter> <filter-mapping> <filter-name>AMFMessageFilter</filter-name> <url-pattern>/graniteamf/*</url-pattern> </filter-mapping> <!-- gestion des requêtes AMF (execution) --> <servlet> <servlet-name>AMFMessageServlet</servlet-name> <servlet- class>org.granite.messaging.webapp.AMFMessageServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>AMFMessageServlet</servlet-name> <url-pattern>/graniteamf/*</url-pattern> </servlet-mapping>

- 15 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 366: Ejb Jsf Struts Flex Jasper

Enfin, il faut copier les librairies granite.swc et granite­essentials.swc dans le répertoire flex_libs. Elles se trouvent dans le répertoire graniteds\build de la distribution. Ces deux librairies contiennent les versions ActionScript des collections persistantes JPA/Hibernate, elles sont donc nécessaires pour gérer correctement des entitiés JPA possédant des associations.

Il est important de linker granite­essentials.swc avec l’option de compilation ­include­libraries flex_libs/granite­essentials.swc, le mode de link par défaut de Flex Builder n’étant pas suffisant.

Allez dans les Propriétes du projet VenteEnLigneFlexGranite, puis dans Flex Compiler et entrez les arguments de compilation suivants :

-services "K:/ENI/DeveloppementEJB3/workspaceGalileo/VenteEnLigneFlexGranite/WebCont ent/WEB-INF/flex/services-config.xml" -locale en_US -include-libraries ../flex_libs/granite-essentials.swc

Ces arguments indiquent également au compilateur où se trouve le fichier services­config.xml qui définit les destinations distantes.

5. Layout de l’application

Le fichier principal de l’application est VenteEnLigneFlexGranite.mxml. Il est entouré du tag <mx:Application>.

Dans l’application, le composant de navigation ViewStack est utilisé pour afficher le panel de login/inscription (identifiant vueLogin) et le panel sécurisé (identifiant VueSecurisee) qui est affiché une fois que l’utilisateur est connecté.

- 16 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 367: Ejb Jsf Struts Flex Jasper

Le fichier VenteEnLigneFlexGranite.mxml contient des composants et du code ActionScript pour gérer la connexion.

<?xml version="1.0" encoding="iso-8859-1"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" xmlns:comps="composants.*" > <mx:Script> <![CDATA[ import composants.panier; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.utils.ObjectUtil; import mx.controls.Alert; import mx.core.Application; [Bindable] public var date:Date = new Date(); private function gestionLoginSucces(eventObj:Event):void Alert.show("Connexion réussie !"); //changer le child actif de la ViewStack appVue.selectedChild = VueSecurisee ; ]]> </mx:Script> <mx:Style source="css/styles.css" /> <mx:DateFormatter id="formatDate" formatString="DD/MM/YYYY" /> <mx:ViewStack id="appVue" width="1000" height="642.1212" x="-4.45" y="9.1"> <mx:Panel horizontalAlign="center" verticalAlign="middle" backgroundColor="#0b333c" id="vueLogin" width="1000" height="666.21216"> <mx:HBox horizontalCenter="0" verticalAlign="middle" width="842.27277" height="586.0606"> <mx:VBox textAlign="center" height="205"> <mx:Image source="images/flash.png" width="186" height="164" /> <mx:Label text="Adobe Flash Player" height="22" color="#F8FAFB" width="181" click="navigateToURL(new URLRequest(’http://www.adobe.com/products/flashplayer/’), ’_blank’)" useHandCursor="true" buttonMode="true" mouseChildren="false"/> </mx:VBox> <mx:Panel width="395.60608" height="565.98486" title="Veuillez vous identifier :" horizontalAlign="center" verticalAlign="middle" titleIcon="@Embed(source=’images/login2.jpg’)" id="panelLogin"> <comps:LoginForm id="loginLog" loginSucces="gestionLoginSucces(event);" width="362.27274" height="515.98486" /> </mx:Panel> <mx:VBox textAlign="center" width="214" height="215"> <mx:Image source="images/Fx.png" width="196" height="174" /> <mx:Label text="Adobe Flex" height="21" color="#F8FAFB" width="160" click="navigateToURL(new URLRequest(’http://www.adobe.com/products/flex/’), ’_blank’)" useHandCursor="true" buttonMode="true" mouseChildren="false"/> </mx:VBox> </mx:HBox>

- 17 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 368: Ejb Jsf Struts Flex Jasper

</mx:Panel> <mx:Panel id="VueSecurisee" title="Vente en ligne avec Flex, GraniteDS et les EJB" titleStyleName="titreStyle"> <mx:ApplicationControlBar width="100%" horizontalAlign="right"> <mx:Label text="Nous sommes le : formatDate.format(date)" textAlign="center"/> <mx:Image source="images/Utilisateur.gif" /> <mx:Label text="Utilisateur : loginLog.client.nom loginLog.client.prenom" textAlign="right" /> <mx:Image source="images/flex.png" / <mx:Image source="images/java.png" /> <mx:Spacer width="100%"/> <mx:Image source="images/panier-shop.jpg" /> <mx:Label id="labelMtt" text="Montant du panier : ecrprincipal.lepanier.mttTotal euros" textAlign="right" /> <mx:TextInput id="rechercheArt" text="Rechercher un article :" /> <mx:Image source="images/rechercher.png"/> </mx:ApplicationControlBar> <comps:ecranPrincipal id="ecrprincipal"/> </mx:Panel> </mx:ViewStack> </mx:Application>

En cas de connexion réussie, la vue avec l’identifiant VueSecurisee est affichée. Celle­ci contient le composant personnalisé ecranPrincipal, préfixé par le namespace comps qui est mappé au répertoire /composants, comme indiqué dans l’écran suivant qui affiche l’arborescence finale de l’application :

Un composant personnalisé offre la possibilité de réutiliser un ensemble de composants mais également de mieux découper l’application. Il peut être généré dans un fichier .SWC qui correspond à une librairie de composants et qu’il est possible de réutiliser dans un autre projet en l’ajoutant dans le build path de ce projet.

- 18 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 369: Ejb Jsf Struts Flex Jasper

Le composant personnalisé ecranPrincipal fait usage du composant ToggleButtonBar pour pouvoir naviguer à travers les vues empilées dans la ViewStack dont l’identifiant est maViewStack :

Fichier ecranPrincipal.mxml :

<?xml version="1.0" encoding="utf-8"?> <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" xmlns:comps="composants.*" > <mx:Script> <![CDATA[ import mx.core.Application; import mx.controls.Alert; private function logout():void ... ]]> </mx:Script> <mx:VBox width="188" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" > <mx:Label text="MENU" /> <mx: ToggleButtonBar id="buttonBar" direction="vertical" dataProvider="maViewStack" themeColor="haloOrange" fontSize="16" width="186" height="439" /> </mx:VBox> <mx:ViewStack id="maViewStack" width="100%" height="100%" backgroundAlpha="0.0"> <mx:VBox id="conteneur1" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Accueil" icon="@Embed(’../images/accueil.png’)"> <comps:ecranAccueil width="100%" /> </mx:VBox> <mx:VBox id="conteneur2" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Mon profil" icon="@Embed(’../images/profil.png’)"> <comps:profil width="100%" /> </mx:VBox> <mx:VBox id="conteneur3" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Liste des articles" icon="@Embed(’../images/listeArticles.png’)"> <comps:listeArticles width="100%" /> </mx:VBox> <mx:VBox id="conteneur4" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Historique" icon="@Embed(’../images/historique.png’)"> <comps:historique width="100%" />

- 19 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 370: Ejb Jsf Struts Flex Jasper

</mx:VBox> <mx:VBox id="conteneur5" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Mon panier" icon="@Embed(’../images/panier.png’)"> <comps:panier width="100%" id="lepanier" /> </mx:VBox> <mx:VBox id="conteneur6" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Recherche" icon="@Embed(’../images/recherche.png’)"> <comps:recherche width="100%" /> </mx:VBox> <mx:VBox id="conteneur7" width="100%" height="100%" borderStyle="solid" borderThickness="1" borderColor="#D2D9DB" dropShadowEnabled="true" shadowDirection="center" paddingBottom="0" paddingTop="5" label="Deconnexion" click="logout()"icon="@Embed(’../images/deconnexion.png’)"> </mx:VBox> </mx:ViewStack> </mx:HBox>

Le rendu visuel est illustré par l’écran suivant :

Le composant personnalisé ecranAccueil est le composant affiché par défaut, lorsque l’utilisateur s’est authentifié, car c’est celui qui est en haut de la ViewStack.

Pour afficher les accents, l’encodage à utiliser est ISO­8859­1 (Western Europe). Il faut le spécifier au début de chaque fichier MXML avec la ligne suivante : <?xml version="1.0" encoding="iso­8859­1"?>

- 20 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 371: Ejb Jsf Struts Flex Jasper

6. Authentification à l’application

La connexion est gérée à l’intérieur du composant personnalisé LoginForm. La création d’un composant personnalisé est facilitée par le plugin Flex Builder puisqu’il offre un wizard pour sélectionner le composant de base, ainsi que les dimensions et le nom :

Dans le composant LoginForm.mxml, la méthode ActionScript connexion() est définie :

<?xml version="1.0" encoding="iso-8859-1"?> <mx:Form xmlns:mx="http://www.adobe.com/2006/mxml" width="266" height="550" > <mx:Metadata> [Event(name="loginSucces", type="flash.events.Event")] </mx:Metadata> <mx:Script> <![CDATA[ import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.utils.ObjectUtil; import mx.controls.Alert; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.entities.Adresse; import mx.rpc.AsyncToken; import mx.rpc.AsyncResponder;

- 21 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 372: Ejb Jsf Struts Flex Jasper

import mx.rpc.IResponder; import mx.core.Application; [Bindable] public var client:Utilisateur; [Bindable] public var adresse:Adresse; [Bindable] public var titres: Array = [ label:"Mr", data:1, label:"Mme", data:2, label:"Mlle", data:3 ]; private function connexion():void var token:AsyncToken = srv.findUtilisateurByLoginPwd(userName.text, password.text); var myResponder:AsyncResponder = new AsyncResponder(resultHandler, faultHandler); token.addResponder(myResponder); private function resultHandler( event:ResultEvent, token:AsyncToken):void var utilisateur:Utilisateur= event.result as Utilisateur; if (null != utilisateur) Alert.show("Connexion reussie!"); dispatchEvent(new Event("loginSucces")); client = utilisateur; adresse = client.adresseFk; else Alert.show("Echec de la connexion !"); public function faultHandler(error:Object, token:AsyncToken):void Alert.show("Erreur vaut : " + error.toString()); ]]> </mx:Script> <mx:RemoteObject id="srv" showBusyCursor="true" destination="destFacadeBean" /> ... <mx:FormHeading id="leheading" label="Formulaire de connexion" /> <mx:FormItem label="Login:" required="true" > <mx:TextInput id="userName" /> </mx:FormItem> <mx:FormItem label="Password:" required="true"> <mx:TextInput id="password" displayAsPassword="true" /> </mx:FormItem> <mx:FormItem width="237"> <mx:Button id="loginBtn" label="Login" icon="@Embed(source=’../images/login.png’)" enabled="(userName.text.length == 0 || password.text.length == 0) ? false : true" toolTip="loginBtn.enabled == true ? ’Cliquez pour soumettre’ : ’Entrez le login et le password’" click="connexion()" width="112" height="45"/> </mx:FormItem> <mx:LinkButton id="lienInscription" label="S’inscrire" height="20" click="currentState=’Inscription’"/> </mx:Form>

Cette méthode connexion() appelle le stateful session bean FacadeBean.

Le schéma suivant montre le lien entre le canal de communication GraniteDS (my­graniteAMF) défini dans le fichier

- 22 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 373: Ejb Jsf Struts Flex Jasper

services­config.xml et l’appel du stateful session bean FacadeBean depuis cette méthode ActionScript connexion() :

Les EJB sont utilisés dans la couche métier de l’application pour réaliser les traitements métiers. Dans la couche présentation, les traitements sont réalisés avec les fichiers MXML et les classes ActionScript 3. Le schéma suivant représente l’architecture mise en place :

- 23 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 374: Ejb Jsf Struts Flex Jasper

Les appels distants de services (EJB et autres) avec Flex se font de façon asynchrone, ce qui signifie que les durées des appels ne sont pas connues ou quel appel de service sera le premier terminé si plusieurs appels de services sont exécutés. Pour gérer cette nature asynchrone des appels de services dans Flex, une solution consiste à utiliser la classe AsyncToken. Une classe AsyncToken permet d’attacher des méthodes responder à l’appel du service de sorte que le token appelle ces méthodes responder lorsque l’exécution du service se termine.

Il faut importer la classe mx.rpc.AsyncToken et l’interface mx.rpc.IResponder. La méthode resultHandler(event:ResultEvent, token:AsyncToken) est une méthode privée qui capture l’évènement qui est passé en paramètre. Elle indique que l’appel distant d’un service a retourné un résultat avec succès.

Gestion des appels asynchrones de méthodes :

Un évènement nommé loginSucces, de type flash.events.Event, est déclaré à l’intérieur du tag <mx:Metadata>. En cas de connexion réussie, cet évènement est dispatché grâce à la méthode dispatchEvent() depuis la méthode resultHandler(event:ResultEvent, token:AsyncToken).

La méthode faultHandler(error:Object, token:AsyncToken) gère les erreurs qui peuvent survenir. Elle peut par exemple afficher un message d’erreur dans une pop­up.

La méthode gestionLoginSucces (eventObj:Event) dans le fichier VenteEnLigneFlexGranite.mxml est à l’écoute de cet évènement et change la vue active du composant ViewStack pour afficher la vue VueSecurisee :

Extrait du fichier VenteEnLigneFlexGranite.mxml :

- 24 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 375: Ejb Jsf Struts Flex Jasper

private function gestionLoginSucces(eventObj:Event):void Alert.show("Connexion réussie !"); //changer le child actif de la ViewStack appVue.selectedChild = VueSecurisee ;

7. Lister les articles dans un composant DataGrid

Le composant <mx:DataGrid> (grille de données) est l’un des composants les plus utilisés. Il permet de facilement afficher des données sur plusieurs colonnes. Ces données sont spécifiées dans la propriété dataProvider. Le composant listeArticles.mxml appelle, dès que l’instanciation est terminée, la méthode findBoutique() du stateful session bean FacadeBean pour alimenter cette propriété dataProvider du composant <mx:DataGrid> :

Le code du composant listeArticles.mxml est le suivant :

<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="450" creationComplete="srv.findBoutique()"> <mx:Script> <![CDATA[ import mx.controls.Alert; import mx.managers.PopUpManager; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Lignecommande; import mx.rpc.AsyncToken; import mx.rpc.AsyncResponder; import mx.rpc.IResponder; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; private function afficherDetails():void var articleIdChoisi:int; articleIdChoisi = listeDesArticles.selectedItem.id; var token:AsyncToken = srvPanier.findById(articleIdChoisi); var myResponder : AsyncResponder = new

- 25 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 376: Ejb Jsf Struts Flex Jasper

AsyncResponder(resultHandler, faultHandler); token.addResponder(myResponder); private function resultHandler( event:ResultEvent, token:AsyncToken):void var article:Article = event.result as Article; var popupDetails:detailsArticle = new detailsArticle(); popupDetails.articleDetails = article; PopUpManager.addPopUp(popupDetails, parent.parent, true); PopUpManager.centerPopUp(popupDetails); public function faultHandler(error:Object, token:AsyncToken):void Alert.show("Erreur vaut : " + error.toString()); ]]> </mx:Script> <mx:RemoteObject id="srv" showBusyCursor="true" destination="destFacadeBean" /> <mx:RemoteObject id="srvPanier" showBusyCursor="true" destination="destPanierBean" /> <!-- Affichage des articles dans une DataGrid --> <mx:DataGrid id="listeDesArticles" dataProvider="srv.findBoutique.lastResult" width="100%" height="100%" click="afficherDetails()" > <mx:columns> <mx:DataGridColumn headerText="Article ID" dataField="id" /> <mx:DataGridColumn headerText="Nom article" dataField="nomArticle" /> <mx:DataGridColumn headerText="Catalogue" dataField="nomCatalogue" /> <mx:DataGridColumn headerText="Produit" dataField="nomProduit" /> <mx:DataGridColumn headerText="Prix TTC" dataField="prix" /> <mx:DataGridColumn headerText="Quantite en stock" dataField="quantite" /> </mx:columns> </mx:DataGrid> </mx:Canvas>

Les colonnes du composant DataGrid sont créées avec les tags <mx:DataGridColumn>. La propriété dataField du tag <mx:DataGridColumn> définit le nom de l’élément du dataProvider qui est associé avec la colonne. Les six colonnes affichées correspondent aux champs retournés par la méthode findBoutique() du stateful session bean FacadeBean.

Le composant remoteObject, dont l’identifiant est srv, est mappé avec la destination destFacadeBean. La propriété lastResult fournit un accès au résultat du dernier appel et est utilisée dans la propriété dataProvider de la DataGrid pour l’alimenter.

8. Afficher les détails d’un article dans une pop­up

Les détails d’un article sont affichés dans une pop­up lorsque l’utilisateur clique sur la ligne de l’article, à l’intérieur du composant Datagrid. La ligne sélectionnée est identifiée avec la propriété selectedItem. L’id de l’article est ensuite récupéré avec les propriétés de l’objet selectedItem : listeDesArticles.selectedItem.id.

La méthode findById du stateful session bean FacadeBean est ensuite utilisée pour récupérer l’entity bean Article.

Le schéma suivant illustre les appels des différentes méthodes du front­end Flex (MXML / ActionScript 3) et du back­end Java dont les services (EJB) sont exposés via GraniteDS :

- 26 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 377: Ejb Jsf Struts Flex Jasper

Ouverture de la pop­up de détails lors de la sélection d’un article :

Dans le composant listeArticles.mxml, la méthode centerPopUp() de la classe mx.managers.PopUpManager permet de créer et placer une pop­up au centre de l’application :

var popupDetails:detailsArticle = new detailsArticle(); popupDetails.articleDetails = artDetails; PopUpManager.addPopUp(popupDetails, parent.parent, true); PopUpManager.centerPopUp(popupDetails);

La méthode addPopUp() prend trois paramètres :

- 27 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 378: Ejb Jsf Struts Flex Jasper

Le composant de référence.

Le conteneur parent de la pop­up (celui qui contient la pop­up).

Un booléen qui spécifie si la pop­up est modale ou non.

C’est une pop­up modale : elle s’affiche au premier plan dans la fenêtre active et bloque l’arrière­plan en l’empêchant d’avoir le focus et en le recouvrant d’un voile.

Le composant à l’intérieur de la pop­up est un panel défini dans le fichier detailsArticle.mxml :

<?xml version="1.0" encoding="iso-8859-1"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" width="490" height="500"> <!-- Ce composant s’affiche dans une pop-up --> <mx:Script> <![CDATA[ import mx.managers.PopUpManager; import com.eni.dvtejb.metier.entities.Article; import mx.rpc.AsyncToken; import mx.rpc.AsyncResponder; import mx.rpc.IResponder; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.controls.Alert; [Bindable] public var articleDetails:Article; private function ajouterArticleAuPanier():void var laQuantite:Number = qtite.value; var token:AsyncToken = srvPanier.ajouterArticle(articleDetails, laQuantite); var myResponder : AsyncResponder = new AsyncResponder(resultHandler, faultHandler); token.addResponder(myResponder); private function resultHandler( event:ResultEvent, token:AsyncToken):void confirmation.text = "Article ajouté au panier"; public function faultHandler(error:Object, token:AsyncToken):void Alert.show("Erreur : " + error.toString()); ]]> </mx:Script> <mx:RemoteObject id="srvPanier" showBusyCursor="true" destination="destPanierBean" /> <mx:HBox horizontalAlign="center" width="100%"> <mx:Label text="Vente en Ligne" color="red" fontWeight="bold"/> </mx:HBox> <mx:VBox width="100%" height="100%" x="10" y="37" paddingLeft="5" paddingRight="5" horizontalAlign="center" horizontalScrollPolicy="off" verticalGap="2"> <mx:Form paddingTop="0" paddingBottom="0" paddingLeft="0" paddingRight="0" borderStyle="solid">

- 28 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 379: Ejb Jsf Struts Flex Jasper

<mx:FormHeading label="Détails de l’article"/> <mx:FormItem label="Article ID:"> <mx:Label text="articleDetails.articleid"/> </mx:FormItem> <mx:FormItem label="Produit:"> <mx:Label text="articleDetails.produitFk.nom"/> </mx:FormItem> <mx:FormItem label="Nom:"> <mx:Label id="article" text="articleDetails.nom" width="300"/> </mx:FormItem> <mx:FormItem label="Prix unitaire:"> <mx:Label text="articleDetails.prix"/> </mx:FormItem> <mx:FormItem label="Quantité en stock:"> <mx:Label text="articleDetails.stockFK.quantite"/> </mx:FormItem> <mx:FormItem label="Image:"> <mx:Image id="img" maxWidth="100" maxHeight="123" source="articleDetails.image"/> </mx:FormItem> <mx:FormItem label="Quantité souhaitée:"> <mx:NumericStepper id="qtite" maxWidth="100" maxHeight="123" /> </mx:FormItem> </mx:Form> </mx:VBox> <mx:ControlBar width="100%" paddingBottom="15" horizontalAlign="center"> <mx:Button id="panierBouton" label="Ajouter au panier" click="ajouterArticleAuPanier()"/> <mx:Button label="Fermer" click="PopUpManager.removePopUp(this)" width="75"/> </mx:ControlBar> <mx:HBox > <mx:Label id="confirmation" text=""/> </mx:HBox> </mx:Panel>

Le composant <mx:Image> est utilisé pour afficher l’image d’un article. La source de cette image est spécifiée dans la propriété source (l’attribut image de l’entity bean Article).

La méthode PopUpManager.removePopUp(this), invoquée par un clic sur le bouton labellisé « Fermer », prend pour paramètre l’instance de la pop­up à supprimer, c’est­à­dire le composant lui­même dans ce cas.

À l’intérieur de la pop­up, un bouton permet d’ajouter l’article dans le panier, avec la quantité souhaitée. Cette quantité est choisie à l’aide du composant <mx:NumericStepper> qui utilise un ascenseur pour incrémenter/décrémenter la valeur.

9. Supprimer un article du panier

L’ajout d’un article se fait dans la pop­up de détails d’un article. En revanche la suppression d’un article se fait, de façon classique, directement depuis la DataGrid qui liste les articles présents dans le panier.

Panier.mxml

<?xml version="1.0" encoding="iso-8859-1"?> <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="400" height="300" creationComplete="init()" title="Panier" horizontalAlign="center" > <mx:Script> <![CDATA[ import mx.core.Application;

- 29 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 380: Ejb Jsf Struts Flex Jasper

import mx.collections.ArrayCollection import mx.controls.Alert; import com.eni.dvtejb.metier.services.ArticlePanier; import com.eni.dvtejb.metier.entities.Article; import modeles.ArticlePanierModele; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.rpc.AsyncToken; import mx.rpc.AsyncResponder; import mx.rpc.IResponder; [Bindable] private var articlesPanier:ArrayCollection = new ArrayCollection(); [Bindable] private var articlesPanierModele:ArrayCollection = new ArrayCollection(); [Bindable] public var mttTotal:Number = 0; private function init():void var token:AsyncToken = srvPanier.getPanier(); var myResponder : AsyncResponder = new AsyncResponder(resultHandler, faultHandler); token.addResponder(myResponder); calculMttTotal(); private function calculMttTotal():void var token2:AsyncToken = srvPanier.getMontantTotal(); var myResponder2 : AsyncResponder = new AsyncResponder(resultMttTotalHandler, faultHandler); token2.addResponder(myResponder2); private function resultHandler( event:ResultEvent, token:AsyncToken):void articlesPanier = event.result as ArrayCollection; var index:int; for ( index = 0; index < articlesPanier.length; index ++) var artPanierModele:ArticlePanierModele = new ArticlePanierModele(); artPanierModele.articleId = articlesPanier[index].article.articleid; artPanierModele.articleNom = articlesPanier[index].article.nom; artPanierModele.quantite = articlesPanier[index].quantite; artPanierModele.quantite = articlesPanier[index].quantite; artPanierModele.ssTotal = articlesPanier[index].quantite * articlesPanier[index].article.prix; articlesPanierModele.addItem(artPanierModele); private function resultMttTotalHandler( event:ResultEvent, token:AsyncToken):void mttTotal = event.result as Number; lemttTotal.text = String(mttTotal); public function faultHandler(error:Object, token:AsyncToken):void Alert.show("Erreur vaut : " + error.toString());

- 30 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 381: Ejb Jsf Struts Flex Jasper

// Supprime un article du panier public function supprimerArticlePanier():void var artPanierModele:ArticlePanierModele = ArticlePanierModele(listeDesArticlesPanier.selectedItem); var artId:Number = artPanierModele.articleId; var articleP: ArticlePanier; var index:int; for ( index = 0; index < articlesPanier.length; index ++) articleP = articlesPanier[index]; if (artId == articleP.article.articleid) break; var token:AsyncToken = srvPanier.supprimerArticle(articleP); var myResponder : AsyncResponder = new AsyncResponder(resultSuppressionHandler, faultHandler); token.addResponder(myResponder); //recalcul du montant total calculMttTotal(); private function resultSuppressionHandler( event:ResultEvent, token:AsyncToken):void articlesPanierModele.removeItemAt(listeDesArticlesPanier.selectedIndex); if(listeDesArticlesPanier.selectedIndex==0) listeDesArticlesPanier.selectable=false; public function viderPanier():void var token:AsyncToken = srvPanier.viderPanier(); var myResponder : AsyncResponder = new AsyncResponder(resultViderHandler, faultHandler); token.addResponder(myResponder); //recalcul du montant total calculMttTotal(); private function resultViderHandler( event:ResultEvent, token:AsyncToken):void articlesPanierModele.removeAll(); ]]> </mx:Script> <mx:RemoteObject id="srvPanier" showBusyCursor="true" destination="destPanierBean" /> <mx:DataGrid id="listeDesArticlesPanier" dataProvider="articlesPanierModele" rowHeight="35" width="100%" height="100%" > <mx:columns> <mx:DataGridColumn headerText="Article ID" dataField="articleId" /> <mx:DataGridColumn headerText="Nom article" dataField="articleNom" /> <mx:DataGridColumn headerText="Quantité" dataField="quantite" /> <mx:DataGridColumn headerText="Quantité" dataField="quantite" id="laQtite"/> <mx:DataGridColumn headerText="Sous-total" dataField="ssTotal" /> <mx:DataGridColumn headerText="" > <mx:itemRenderer>

- 31 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 382: Ejb Jsf Struts Flex Jasper

<mx:Component> <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalAlign="center"> <mx:Script> <![CDATA[ import mx.controls.Alert; import mx.core.Application; private function supprimerArtPanier():void outerDocument.supprimerArticlePanier(); ]]> </mx:Script> <mx:Button label="Supprimer" click="supprimerArtPanier()" icon="@Embed(’../images/poubelle.gif’)" /> </mx:HBox> </mx:Component> </mx:itemRenderer> </mx:DataGridColumn> </mx:columns> </mx:DataGrid> <mx:HBox> <mx:Label text="Montant total :" /> <mx:Text id="lemttTotal" text="" /> </mx:HBox> <mx:HBox> <mx:Button label="Commander" height="35" /> <mx:Button label="Vider le panier" click="viderPanier()" icon="@Embed(’../images/viderPanier.png’)" /> </mx:HBox> </mx:Panel>

Le bloc de code délimité par les tags <mx:Component> et </mx:Component> définit une nouvelle portée. L’accès à un élément à l’extérieur de cette portée se fait en préfixant cet élément avec le mot­clé outerDocument. C’est le cas de l’appel de la méthode supprimerArticlePanier() car le bouton de suppression se trouve dans un composant <mx:Component> qui possède son propre scope.

Le composant <mx:itemRenderer> encadre le composant <mx:Component> et permet de personnaliser le contenu d’une colonne, en l’occurrence la quatrième colonne, ce qui explique la présence du tag <mx:Button>.

Un article du panier est modélisé dans une classe ActionScript appelée ArticlePanierModele, dans le package modeles. Voici le contenu du fichier ArticlePanierModele.as :

package modeles public class ArticlePanierModele public function ArticlePanierModele() public var articleId:Number; public var articleNom:String; public var quantite:Number; public var quantite:Number; public var ssTotal:Number;

Cette modélisation de la classe ArticlePanier en tant qu’objet ActionScript facilite la gestion des articles (chargement et suppression).

Le composant DataGrid qui contient la liste des articles du panier est alimentée par une collection d’objets de type ArticlePanierModele.

Dans une classe Java, par défaut, toutes les variables membres non statiques et qui ne sont pas explicitement exclues de la sérialisation à l’aide du mot­clé transient (qu’elles soient privées, protégées ou publiques) sont

Sérialisation et externalisation

- 32 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 383: Ejb Jsf Struts Flex Jasper

sérialisées. C’est la sérialisation standard et dans ce cas la classe sérialisée implémente l’interface java.io.Serializable. Si l’on souhaite personnaliser cette sérialisation afin de ne sérialiser que certaines propriétés (ou sérialiser ces propriétés différemment, ou encore ajouter des méta­informations), une solution consiste à implémenter l’interface java.io.Externalizable qui étend l’interface java.io.Serializable. Cette interface force à implémenter deux méthodes pour contrôler la sérialisation. Ces deux méthodes sont :

readExternal(ObjectInput in)

writeExternal(ObjectOutput out)

L’externalisation permet donc de contrôler le processus de sérialisation (à noter qu’il est également possible d’implémenter des méthodes readObject(ObjectInputStream in) et writeObject(ObjectOutputStream out) pour personnaliser la sérialisation standard, mais que cette possibilité n’a aucune contrepartie dans Flex).

Concernant Flex, la sérialisation AMF3 standard ne sérialise que les propriétés publiques non statiques et non transients : seules les variables membres publiques et les propriétés get/set publiques sont prises en compte (celles qui correspondent donc à la spécification Java Bean). Les variables membres privées ou protégées, strictement inaccessibles ou en lecture seule (méthode get publique sans équivalent en écriture) sont ignorées. Pour pallier à cette limitation, la sérialisation AMF3 supporte le mécanisme de l’externalisation en proposant la contrepartie de l’interface java.io.Externalizable : l’interface flash.utils.IExternalizable, avec ses deux méthodes readExternal / writeExternal correspondantes. Lorsque la sérialisation AMF3 rencontre une classe Java qui implémente l’interface java.io.Externalizable, elle utilise un encodage particulier, distincte de la sérialisation standard, et utilise les méthodes readExternal / writeExternal disponibles. Côté Flex, la sérialisation de la classe ActionScript3 correspondante qui doit implémenter l’interface flash.util.IExternalizable, utilise elle aussi les méthodes readExternal / writeExternal fournies. L’implémentation des lectures­écritures côté Flex et Java est alors librement personnalisable, mais doit rester cohérente (lecture et écriture des champs dans le même ordre).

GraniteDS propose un mécanisme d’externalisation simplifié qui permet d’avoir l’équivalent de la sérialisation Java standard (toutes les variables membres non statiques et non transients, privées, protégées ou publiques), sans avoir à implémenter explicitement l’interface java.io.Externalizable. Plus encore, le générateur de code de GraniteDS (Gas3) automatise l’écriture de classes ActionScript3 qui implémentent l’interface flash.utils.IExternalizable : cette génération se fait sur la base de l’analyse réflexive des classes Java compilées et reproduit les mêmes variables membres, avec les mêmes méthodes d’accès (certaines propriétés peuvent ainsi rester totalement opaques ou en lecture seule côté Flex). Il est même possible, et c’est le cas pour la sérialisation des Entity Beans, de sérialiser des méta­informations complémentaires, liées, par exemple, à l’état d’initialisation du bean, de façon à conserver toute l’information d’un objet persistent détaché. Lorsque ce générateur est utilisé, les classes Java utilisées pour la génération, qui n’implémentent pas l’interface java.io.Externalizable, doivent pourtant être sérialisées comme si elles implémentaient cette interface. Il faut alors informer la sérialisation de GraniteDS que ces classes doivent être externalisées. C’est ce qu’il est possible de faire en modifiant le fichier granite­config.xml ou en annotant la classe Java avec @ExternalizedBean. Cette configuration faite, GraniteDS simulera par réflexion la sérialisation d’une classe implémentant l’interface java.io.Externalizable et son équivalent généré côté Flex lira et écrira les données sérialisées dans un ordre similaire.

Un message d’erreur du type suivant peut donc apparaître :

ERROR [AMFMessageFilter] AMF message error org.granite.messaging.amf.io.AMF3SerializationException at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deseria lizer.java:94) ... Caused by: java.lang.RuntimeException: The ActionScript3 class bound to com.eni.dvtejb.metier.services.ArticlePanier (ie: [RemoteClass(alias="com.eni.dvtejb.metier.services.ArticlePanier")]) implements flash.utils.IExternalizable but this Java class neither implements java.io.Externalizable nor is in the scope of a configured externalizer (please fix your granite-config.xml).

Ce message d’erreur concerne la classe ArticlePanier.java qui sert à stocker les articles et leurs quantités dans le panier et qui n’implémente pas l’interface java.io.Externalizable :

package com.eni.dvtejb.metier.services; import java.io.Serializable; import java.math.BigDecimal; import com.eni.dvtejb.metier.entities.Article; public class ArticlePanier implements Serializable

- 33 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 384: Ejb Jsf Struts Flex Jasper

private static final long serialVersionUID = 1L; public ArticlePanier() super(); private Article article; private BigDecimal quantite; public ArticlePanier(Article article, BigDecimal quantite) this.setArticle(article); this.setQuantite(quantite); public void setQuantite(BigDecimal quantite) this.quantite = quantite; public BigDecimal getQuantite() return quantite; public void setArticle(Article article) this.article = article; public Article getArticle() return article;

L’explication de l’erreur est la suivante: la sérialisation d’une classe ActionScript3 qui implémente IExternalizable (sérialisation de Flex vers Java), lorsque la classe Java correspondante (ArticlePanier.java) n’implémente pas Externalizable (ou n’est pas configurée comme externalisable dans GDS) est inutilisable. On ne peut pas deviner comment et dans quel ordre les données ont été sérialisées (ce n’est pas forcément une classe AS3 générée, il peut s’agir d’une classe IExternalizable écrite à la main), et GDS lance donc une exception lors de la dé­sérialisation.

Cette erreur peut être résolue de plusieurs façons :

1 ­ Soit en excluant la classe ArticlePanier.java de la configuration du builder GAS3 (propriétés du projet, GraniteDS, onglet « Java Sources », option « Excluded » et en ajoutant le pattern « **/ArticlePanier.java ») et en supprimant les classes ActionScript ArticlePanier.as et ArticlePanierBase.as.

Il est ensuite nécessaire d’écrire à la main une seule classe ArticlePanier.as :

- 34 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 385: Ejb Jsf Struts Flex Jasper

<package com.eni.dvtejb.metier.services import com.eni.dvtejb.metier.entities.Article; [Bindable] [RemoteClass(alias="com.eni.dvtejb.metier.services.ArticlePanier")] public class ArticlePanier public var article:Article; public var quantite:Number;

Cette classe, qui n’est pas IExternalizable, utilisera la sérialisation standard, ce qui n’est pas un problème pour le bean ArticlePanier.java qui n’a que des propriétés publiques (méthodes getter/setter pour les deux variables).

2 ­ Soit en conservant les classes ActionScript générées et en modifiant le fichier granite­config.xml pour lui ajouter un externalizer de type ArticlePanier :

<externalizers> <externalizer type="org.granite.hibernate.HibernateExternalizer"> <include annotated-with="javax.persistence.Entity"/> <include annotated-with="javax.persistence.MappedSuperclass"/> <include annotated-with="javax.persistence.Embeddable"/> <include type="com.eni.dvtejb.metier.services.ArticlePanier"/> </externalizer> <externalizer type="org.granite.messaging.amf.io.util.externalizer.EnumExternalizer"> <include instance-of="java.lang.Enum"/> </externalizer> </externalizers>

La classe générée initialement ArticlePanierBase.as implémente l’interface flash.utils.IExternalizable :

package com.eni.dvtejb.metier.services import com.eni.dvtejb.metier.entities.Article; import flash.utils.IDataInput; import flash.utils.IDataOutput; import flash.utils.IExternalizable; [Bindable] public class ArticlePanierBase implements IExternalizable private var _article:Article; private var _quantite:Number; ... // getters / setters public function readExternal(input:IDataInput):void _article = input.readObject() as Article; _quantite = function(o:*):Number return (o is Number ? o as Number : Number.NaN) (input.readObject()); public function writeExternal(output:IDataOutput):void output.writeObject(_article); output.writeObject(_quantite);

3 ­ Enfin une autre solution consiste à annoter la classe ArticlePanier.java avec l’annotation @ExternalizedBean présente dans le package org.granite.messaging.amf.io.util.externalizer.annotation. Cette solution ne requiert pas de modification du fichier granite­config.xml.

Le contenu du panier doit pouvoir être modifié par le client. Chaque article peut donc être supprimé en cliquant sur le bouton Supprimer et la collection articlesPanierModele de type ArrayCollection, dans le composant panier.mxml, est alors mise à jour. La méthode removeItemAt(index:int) de la classe ArrayCollection supprime de la collection l’article dont l’index est passé en paramètre. Le databinding entre la propriété dataProvider de la DataGrid et la variable articlesPanierModele, spécifié avec l’annotation [Bindable], met automatiquement à jour le contenu de cette DataGrid.

- 35 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 386: Ejb Jsf Struts Flex Jasper

10. Rechercher un article

La recherche d’un article est faite dans le composant recherche.mxml. Elle permet de faire une recherche d’articles à partir des premières lettres entrées par l’utilisateur.

Afin de mettre en œuvre la fonctionnalité de recherche désirée, il faut créer la méthode rechercheArticleParNom(String nomArticle) dans le stateless session bean RechercheBean.

Extrait de RechercheBean.java :

public List<Article> rechercheArticleParNom(String nomArticle) Query query = null; query = entityManager.createNativeQuery("SELECT a.articleid, a.nom, a.prix FROM article a WHERE " + " a.nom like ?1"); query.setParameter(1, nomArticle + "%"); List articles = query.getResultList(); return articles;

Cette méthode exécute une requête SQL pour rechercher l’article qui commence par les lettres fournies en paramètre. L’opérateur LIKE est utilisé avec le signe pourcentage % pour effectuer une recherche en tenant compte de la différence entre lettres majuscules et lettres minuscules.

Dans le composant recherche.mxml, la méthode rechercherArt(nomArt :String) appelle la méthode rechercheArticleParNom(String nomArticle). Voici le contenu du composant recherche.mxml :

<?xml version="1.0" encoding="iso-8859-1"?> <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" label="Recherche" verticalGap="0" > <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.rpc.events.ResultEvent; import mx.rpc.events.FaultEvent; import mx.utils.ObjectUtil;

- 36 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 387: Ejb Jsf Struts Flex Jasper

import mx.controls.Alert; import mx.rpc.AsyncToken; import mx.rpc.AsyncResponder; import mx.rpc.IResponder; import com.eni.dvtejb.metier.entities.Article; [Bindable] private var articlesResultat:ArrayCollection = new ArrayCollection(); private var lesArtResultat:ArrayCollection = new ArrayCollection(); public function rechercherArt(nomArt:String):void var token:AsyncToken = srv.rechercheArticleParNom(nomArt); var myResponder : AsyncResponder = new AsyncResponder(resultHandler, faultHandler); token.addResponder(myResponder); public function resultHandler( event:ResultEvent, token:AsyncToken):void // Renvoie une collection d’articles lesArtResultat = event.result as ArrayCollection; var index:int; articlesResultat.removeAll(); for ( index = 0; index < lesArtResultat.length; index ++) var art:Article = new Article(); art.articleid = lesArtResultat[index][0]; art.nom = lesArtResultat[index][1]; art.prix = lesArtResultat[index][2]; articlesResultat.addItem(art); public function faultHandler(error:Object, token:AsyncToken):void Alert.show("Erreur vaut : " + error.toString()); ]]> </mx:Script> <mx:RemoteObject id="srv" showBusyCursor="true" destination="destRechercheBean" /> <mx:VBox height="2" width="100%" /> <mx:Label text="Recherche avancée" fontWeight="bold"/> <mx:Label text="Saisissez un article ou les premières lettres d’un article" /> <!-- Recherche --> <mx:Form width="100%"> <mx:FormItem label="Article : "> <mx:TextInput id="req" width="300" /> </mx:FormItem> <mx:Button id="btnRech" label="Rechercher" click="rechercherArt(req.text)" icon="@Embed(’../images/loupe.png’)"/> </mx:Form> <mx:VBox height="2" width="100%" /> <!-- Resultats --> <mx:HDividedBox height="100%" width="100%" >

- 37 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 388: Ejb Jsf Struts Flex Jasper

<mx:VBox height="100%"> <mx:DataGrid id="datagridRes" dataProvider="articlesResultat" height="100%" width="100%" > <mx:columns> <mx:DataGridColumn dataField="articleid" headerText="Article ID" /> <mx:DataGridColumn dataField="nom" headerText="Nom" /> <mx:DataGridColumn dataField="prix" headerText="Prix unitaire" /> </mx:columns> </mx:DataGrid> </mx:VBox> </mx:HDividedBox> </mx:VBox>

Vous remarquerez les points suivants :

La variable liée articlesResultat est de type ArrayCollection qui est l’équivalent du type java.util.List dans Java. Elle nécessite l’import du package mx.collections.ArrayCollection.

La source de données du composant DataGrid est la variable liée articlesResultat.

À chaque nouvelle recherche, la collection est vidée de ses éléments pour mettre à jour les données du composant DataGrid, grâce à la méthode removeAll().

La capture d’écran suivante montre la liste des articles retournés lorsque l’utilisateur effectue une recherche sur des articles dont la première lettre est P :

- 38 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlZi/QRnH82ICwA=-enidentnumber

Page 389: Ejb Jsf Struts Flex Jasper

L’environnement Java

L’IDE Eclipse et le serveur JBoss ont besoin d’une Java Virtual Machine (JVM) pour être exécuté. Cette JVM est disponible dans le JRE (Java Runtime Environment) et le JDK (Java Development Kit).

Le JDK est un ensemble de composants nécessaires à la création de programmes Java (compilateur javac, interpréteur java, générateur de documentation javadoc...). Le JRE est un interpréteur pour permettre d’exécuter des programmes Java. C’est donc un sous­ensemble du JDK.

Téléchargez le kit de développement Java (JDK) sur le site de Sun à l’adresse suivante : http://java.sun.com/javase/downloads/

Décompressez l’archive dans le répertoire de votre choix, par exemple d:\work\jdk1.6.0_07.

Puis, sous DOS, créez une variable d’environnement JAVA_HOME qui pointe sur ce répertoire :

set JAVA_HOME=d:\work\jdk1.6.0_07

Sous Windows XP/2000/Vista il est nécessaire d’ajouter cette variable dans les propriétés système :

Cette variable est utilisée pour démarrer JBoss en ligne de commande (fichier run.bat). En revanche, elle n’est pas référencée dans l’outil de développement Eclipse car il embarque son propre compilateur Java (ECJ, Eclipse Compiler for Java). Ce compilateur supporte la compilation incrémentale et peut être remplacé par le JDK avec Ant.

Eclipse a besoin d’un JRE. Le fichier d’installation est disponible en téléchargement sur le site d’Oracle/Sun mais il faut savoir qu’il est également inclus dans le JDK (dans le répertoire jre). Une fois le fichier décompressé, déclarez­le dans les préférences d’Eclipse, au niveau des JRE installés :

Installation du Java Development Kit

Installation du Java Runtime Environment

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoyXrFdnH82ICwA=-enidentnumber

Page 390: Ejb Jsf Struts Flex Jasper

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MoyXrFdnH82ICwA=-enidentnumber

Page 391: Ejb Jsf Struts Flex Jasper

Le serveur d’application JBoss

1. Installation et configuration

JBoss est un serveur d’application Open Source. La version 6.0.0 M1 est certifiée Java EE 5 et en cours de certification Java EE 6. Elle est disponible depuis décembre 2009. Elle peut être téléchargée gratuitement sur le site de JBoss à l’adresse suivante : http://www.jboss.org/jbossas/downloads/

Décompressez le fichier jboss­6.0.0.M1.zip (environ 157 MB) dans le répertoire de votre choix, par exemple k:\DeveloppementEJB3. La structure du répertoire de JBoss dans k:\DeveloppementEJB3\jboss­6.0.0.M1 est alors la suivante :

Le contenu de chaque répertoire est le suivant :

Les fichiers de logs sont présents dans le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\log. Un fichier server.log est créé et archivé tous les jours. Log4j est le framework de log utilisé dans les applications de cet ouvrage. Il est configurable dans le fichier \server\default\conf\jboss­log4j.xml.

Dans les classes Java, un logger peut être obtenu avec la classe org.apache.log4j.Logger ou la classe org.jboss.logging.Logger. Par exemple :

import org.apache.log4j.Logger; ... @Stateless @Remote (RechercheRemote.class) public class RechercheBean implements RechercheRemote, Serializable private static final Logger log = Logger.getLogger(RechercheBean.class); ...

Des niveaux de log existent pour permettre de n’afficher que des messages correspondant à tel ou tel niveau de criticité (DEBUG, INFO, WARNING, ERROR, FATAL).

Par défaut, JBoss écoute sur le port 8080. Ce port est fréquemment utilisé par d’autres serveurs et applications. Il est donc préférable de le modifier. Le port par défaut de JBoss est définit dans le fichier server.xml qui se trouve dans le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\deploy\jbossweb.sar. Modifiez la ligne suivante,

bin Contient les scripts permettant de lancer et stopper le serveur.

client Contient les librairies JAR requises par les applications clientes externes et qui accèdent à distance à des ressources JNDI.

docs Contient les DTDs et schémas XML décrivant les fichiers de déploiement.

lib Contient les JARs utilisés par le noyau JBoss.

server Ce répertoire est divisé en trois sous­répertoires (all, default, minimal) qui contiennent les différentes configurations du serveur. C’est dans default/deploy que l’on déploie l’archive (JAR, WAR ou EAR) du projet.

Logs

Port d’écoute

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkWsLHlnH82ICwA=-enidentnumber

Page 392: Ejb Jsf Struts Flex Jasper

en remplaçant le port 8080 par 8085, par exemple.

<!-- A HTTP/1.1 Connector on port 8080 --> <Connector protocol="HTTP/1.1" port="8085"

address="$jboss.bind.address" connectionTimeout="20000" redirectPort="8443" />

Les applications (archives WAR et EAR) sont déployées dans le répertoire server\default\deploy. Les versions explosées de ces applications, c’est­à­dire les archives décompressées, se trouvent dans le répertoire \server\default\tmp et ne sont pas automatiquement supprimées lorsque les applications ne sont plus déployées. Pour limiter l’occupation disque, il peut être utile de procéder de temps en temps au nettoyage de ce répertoire \server\default\tmp afin de supprimer ces archives décompressées.

Concernant la localisation des librairies utilisées dans un projet, deux solutions sont possibles :

Les librairies jars peuvent être directement copiées dans le répertoire WEB­INF/lib d’un projet Web. Dans ce cas elles sont incluses dans l’archive générée (WAR ou EAR). Cela ne pose pas de problème par rapport à la taille de l’archive dans la mesure où il n’y a pas de limitation de taille connue pour le déploiement.

Les librairies jars peuvent être copiées dans le répertoire server/default/lib ou common/lib. Dans ce cas elles ne sont pas incluses dans l’archive générée. En revanche il est nécessaire de les ajouter dans le build path du projet, sous Eclipse.

2. Les consoles et JNDI

Pour démarrer le serveur, il faut exécuter le fichier run.bat qui se trouve dans le répertoire bin. Il est possible de démarrer le serveur directement depuis Eclipse.

Si le serveur a démarré correctement, vous pouvez accéder, par défaut sans nécessité de s’authentifier, à la console JMX (Java Management Extensions) qui est disponible à l’URL suivante : http://localhost:8085/jmx­console :

Déploiement

Console JMX

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkWsLHlnH82ICwA=-enidentnumber

Page 393: Ejb Jsf Struts Flex Jasper

JBoss possède également une console web qui utilise une applet Java pour gérer la présentation. Elle s’affiche à l’URL suivante : http://localhost:8085/web­console

Par défaut, JBoss utilise Tomcat comme conteneur web, mais il est possible d’en utiliser d’autres, tel que Jetty.

Le serveur peut être arrêté depuis la console JMX. Pour cela cliquez sur le lien type=Server dans la section jboss.system. Puis invoquez l’opération shutdown en cliquant sur le bouton Invoke.

Une console d’administration est disponible dans JBoss depuis la version 5.1. Elle est accessible depuis l’URL http://localhost:8085/admin­console. Le login et mot de passe par défaut est admin/admin :

Console Web

Console d’administration

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkWsLHlnH82ICwA=-enidentnumber

Page 394: Ejb Jsf Struts Flex Jasper

Un des intérêts de la console JMX est la possibilité d’afficher le contenu de l’espace de nommage JNDI. L’opération à invoquer est list. Le filtre à utiliser est jboss et le lien est service=JNDIView :

JNDI

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkWsLHlnH82ICwA=-enidentnumber

Page 395: Ejb Jsf Struts Flex Jasper

Affichage de l’arbre JNDI :

La documentation des produits intégrés dans JBoss est régulièrement mise à jour à l’adresse URL suivante : http://www.redhat.com/docs/en­US/JBoss_Enterprise_Application_Platform/. Il existe également un forum

d’entraide pour toute question relative à JBoss : http://community.jboss.org/en/jbossas.

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MkWsLHlnH82ICwA=-enidentnumber

Page 396: Ejb Jsf Struts Flex Jasper

L’outil de développement Eclipse

1. Configuration de JBoss dans Eclipse

Eclipse est un IDE (Integrated Development Environment) populaire grâce aux nombreux avantages qu’il offre aux développeurs (complétion de code, templates, intégration avec des outils et des serveurs, extension possible avec les plugins…). Au minimum 2GB de RAM sont recommandés pour faire fonctionner Eclipse de manière confortable.

Récupérez la dernière version d’Eclipse à l’adresse http://www.eclipse.org . L’outil ne requiert pas d’installation, il suffit de décompresser l’archive dans le répertoire de votre choix. Puis de le lancer en cliquant sur le fichier eclipse.exe. Quand vous démarrez Eclipse pour la première fois, vous devez spécifier le chemin d’un répertoire qui servira de workspace, c’est­à­dire l’environnement de travail dans lequel Eclipse va stocker les projets et leurs informations de configuration.

Une fois qu’Eclipse est démarré, vous pouvez ajouter le serveur JBoss dans la configuration Eclipse. Pour cela, allez dans Window ­ Preferences ­ Server ­ Runtime Environments :

Cliquez sur Add puis sélectionnez le serveur JBoss v5.0 :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 397: Ejb Jsf Struts Flex Jasper

Indiquez ensuite le répertoire d’installation de JBoss. Par exemple K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1 :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 398: Ejb Jsf Struts Flex Jasper

Et cliquez sur Finish.

La liste des serveurs configurés dans Eclipse apparaît ensuite dans les préférences. L’écran suivant définit plusieurs configurations du serveur JBoss à l’intérieur d’Eclipse :

Sélectionnez ensuite la perspective Java EE :

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 399: Ejb Jsf Struts Flex Jasper

Puis dans le menu Server faites un clic droit : New ­ Server. Sélectionnez le « server runtime environment » JBoss 5.0.2.

Ce server runtime environment correspond en réalité à la version 6.0.0 M1 de JBoss, précédemment configurée dans Eclipse.

Saisissez JBoss v6.0 M1 dans la case Server name.

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 400: Ejb Jsf Struts Flex Jasper

Cliquez sur Next et modifiez le port : saisissez 8085 à la place de 8080. Cliquez sur Finish.

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 401: Ejb Jsf Struts Flex Jasper

Le serveur a été ajouté dans la vue Servers. Pour vérifier la configuration du serveur, vous pouvez démarrer le serveur à partir d’Eclipse en faisant un clic droit sur le serveur :

Vérifiez dans la console que le démarrage s’est bien passé et qu’il n’y a pas de messages d’erreur. Vous devez voir les lignes suivantes :

19:03:23,411 INFO [ProfileServiceBootstrap] Loading profil : ProfileKey@1e7d484[domain=default, server=default, name=default] 19:03:23,412 INFO [Http11Protocol] D�marrage de Coyote HTTP/1.1 sur http- 127.0.0.1-8085 19:03:23,474 INFO [AjpProtocol] Starting Coyote AJP/1.3 on ajp-127.0.0.1-8009 19:03:23,490 INFO [AbstractServer] JBossAS [6.0.0.M1 (build: SVNTag=JBoss_6_0_0_M1 date=200912040958)] Started in 1m:33s:540ms

Par défaut Eclipse sort en erreur lorsque le serveur met plus de 50 secondes à démarrer :

Dans ce cas, il faut modifier la durée du timeout en la passant à 200 secondes par exemple. Pour cela faites un clic droit sur le serveur et cliquez sur Open pour ouvrir les propriétés du serveur dans Eclipse. Cliquez ensuite sur [Ctrl] S pour sauvegarder les modifications.

Pour installer JBoss en tant que service Windows, vous pouvez utiliser l’outil JavaService, disponible à l’adresse suivante : http://javaservice.objectweb.org

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 402: Ejb Jsf Struts Flex Jasper

Il est également préférable de modifier l’intervalle de publication automatique de l’archive. Par exemple, 120 secondes au lieu de 15 secondes par défaut. Cette modification se fait également dans les propriétés du serveur :

2. Les librairies des EJB 3

Les librairies nécessaires au développement des EJB sont incluses dans des archives jars qui se trouvent dans les répertoires jboss­6.0.0.M1\common\lib et jboss­6.0.0.M1\client de JBoss. Par exemple :

\common\lib\jboss­ejb3_1.jar

\common\lib\jboss­ejb3­cache.jar

\common\lib\jboss­ejb3­common.jar

\common\lib\jboss­ejb3­core.jar

\common\lib\jboss­ejb3­nointerface­spi.jar

\common\lib\jboss­ejb3­nointerface­impl.jar

\common\lib\jboss­ejb3­timerservice­spi.jar

\common\lib\jboss­ejb3­transactions

\common\lib\jboss­ejb­api_3.1.jar

Lors de la création d’un projet EJB utilisant le serveur JBoss, Eclipse inclut automatiquement ces deux répertoires dans le build path du projet, ainsi que les répertoires jboss­6.0.0.M1\server\default\lib et jboss­6.0.0.M1\lib :

- 7 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 403: Ejb Jsf Struts Flex Jasper

3. Le plugin Visual Page Editor pour JSF

Le plugin Visual Page Editor pour JSF fait partie de la suite de plugins JBoss Tools. Vous pouvez soit installer la suite JBoss Tools et donc tous les plugins qui sont inclus dans cette suite ou bien uniquement ce plugin.

Pour installer ce plugin, récupérez­le à l’adresse suivante : https://www.jboss.org/tools/download/dev.html. La description exacte du plugin est « Visual Page Editor ­ for Richfaces, JSF and HTML / CSS ». Le fichier à télécharger est RichFaces­VPE­win32­3.1.0.v200910281724M­H247­M4.zip (35 MB).

Décompressez ce fichier dans le répertoire d’installation d’Eclipse. Les répertoires plugins et features sont mis à jour avec les jars du plugin.

Il est nécessaire de redémarrer Eclipse pour que les modifications soient prises en compte.

4. Versionning dans Eclipse

Eclipse gère un cache limité des versions précédentes des fichiers dans le workspace. Avec ou sans mise en place d’une gestion de configuration, cette fonctionnalité d’Eclipse est très pratique. Elle permet de comparer des versions différentes, revenir en arrière sur des modifications et même de récupérer un fichier supprimé.

Pour comparer un fichier avec des versions précédentes, faites un clic droit sur le fichier dans l’explorateur de projets, sélectionnez Replace With et Local History ou Previous from Local History. Puis sélectionnez la version précédente souhaitée et cliquez sur Replace pour la remplacer avec le fichier actuel.

Pour retrouver un fichier supprimé, créer un nouveau fichier vide portant le même nom, rafraîchissez le projet et comparez­le dans l’historique local pour retrouver le contenu du fichier supprimé.

Le cache est stocké dans des fichiers à l’intérieur du répertoire <workspaceEclipse>/.metadata/.plugins/org.eclipse.core.resources/.history/. L’historique des fichiers est limité à 7 jours dans la mesure où ils sont supprimés au bout du délai d’expiration qui est de 7 jours par défaut.

5. Quelques raccourcis utiles

Voici une liste de raccourcis qui participent à améliorer la productivité lors des développements :

Raccourcis Explications

[Shift] + [Tab] Décaler une sélection de lignes vers la gauche

- 8 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 404: Ejb Jsf Struts Flex Jasper

De plus, une aide contextuelle peut être invoquée à tout moment en pressant la touche [F1].

Enfin, Eclipse offre aussi la possibilité de créer ses propres raccourcis avec l’aide de templates. Par exemple la template suivante crée le raccourci logger pour déclarer une variable log de type org.jboss.logging.Logger au niveau de la classe :

[Ctrl] + o Aller à la méthode

[Ctrl] + d Supprime la ligne courante

[Ctrl] + q Dernière édition

[Ctrl] + [Shift] + f Formate le code

[Ctrl] + l Aller à la ligne

[Alt] + [Flèche en haut]/[Flèche en bas]

Déplace des lignes vers le haut/bas

[Ctrl] + [Shift] + r Ouvre une ressource

[Ctrl] + [Shift] + t Ouvrir un type

[Ctrl] + [Shift] + l Voir la liste des raccourcis

[Ctrl] + [Espace] Complétion de code

[Ctrl] + 1 Résolution rapide

[Alt] + [Shift] + t Refactoriser

[Ctrl] + [Alt] + h Affichage de la hiérarchie

[Ctrl] + t Affichage rapide de la hiérarchie des types

[Ctrl] + e Sélectionner un fichier dans l’éditeur

[Ctrl] + [Shift] + e Sélectionner un fichier ou plusieurs dans l’éditeur

[Ctrl] + [Alt] + [Flèche en haut]/[Flèche en bas]

Copier­coller vers le haut/bas

Sysout + [Espace] Affiche System.out.println();

Syserr + [Espace] Affiche System.err.println();

Systrace + [Espace] Affiche en sortie le nom de la méthode courante

[Ctrl] + k Recherche en avant

[Ctrl] + J Recherche incrémentale

- 9 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 405: Ejb Jsf Struts Flex Jasper

L’ajout de template se fait au niveau des préférences (Window ­ Preferences ­ Java ­ Editor ­ Templates).

6. Debugger avec Eclipse

Dans Eclipse, JBoss peut être démarré en mode debug (clic droit dans la vue des serveurs). Ceci permet de placer des points d’arrêts dans l’application qui tourne sous JBoss. La fenêtre suivante apparaît automatiquement dans la perspective Debug dès que l’application accède à un point d’arrêt :

- 10 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 406: Ejb Jsf Struts Flex Jasper

Un point d’arrêt est défini en effectuant un double clic dans la marge gauche de l’éditeur. Un point bleu apparaît alors pour confirmer visuellement l’emplacement du point d’arrêt. Une fois le programme suspendu, le contenu des variables est consultable dans la vue Variables en haut à droite ou en positionnant le curseur sur la variable à inspecter ce qui va provoquer l’affichage d’une bulle d’aide avec le contenu de la variable.

L’exécution d’un programme pas à pas se fait à l’aide des commandes Step Into ([F5]) ou Step Over ([F6]). Pour continuer jusqu’au prochain point d’arrêt il faut cliquer sur Resume ([F8]). Il est possible de modifier les valeurs de certaines variables et d’évaluer des expressions avant de reprendre l’exécution du programme. La combinaison de touches [Ctrl] + r permet de reprendre l’exécution du programme jusqu’à la ligne indiquée par la position du curseur.

7. Les fichiers de configuration des projets

Eclipse génère automatiquement deux fichiers au format XML lors de la création d’un projet :

.project : ce fichier contient une description du projet avec des informations sur les builders et les natures présents. Les builders sont déclarés à l’intérieur des balises <buildCommand> </buildCommand> et les natures sont déclarées à l’intérieur des balises <nature></nature>. Il est parfois nécessaire d’éditer à la main ce fichier pour supprimer des natures ou des builders.

.classpath : ce fichier décrit les répertoires sources et les librairies externes à inclure dans le classpath d’Eclipse. Il est utilisé pour la validation, la complétion et l’import automatique des classes à l’intérieur de l’IDE Eclipse.

Ces deux fichiers se trouvent à la racine de chaque projet.

- 11 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhkAS5pnH82ICwA=-enidentnumber

Page 407: Ejb Jsf Struts Flex Jasper

La base de données Oracle

1. Installation d’Oracle Database 10g XE Server

Oracle est une base de données relationnelle. La version Oracle Database XE Server est gratuite. Elle peut être téléchargée (environ 160 MB) sur le site de l’éditeur Oracle à l’adresse suivante : http://www.oracle.com/technology/products/database/xe/index.html

Lancez le fichier d’installation et suivez les instructions de la procédure d’installation.

Une fois installée, la console d’administration est accessible à l’URL suivante : http://localhost:8080/apex

Pour la consultation des données, il est fortement conseillé d’utiliser Toad (Tool for Oracle Application Developers). C’est un environnement de développement puissant et léger conçu pour simplifier et accélérer le

développement de base de données et l’administration au quotidien. Une version freeware est disponible : http://www.toadsoft.com.

2. Insertion d’images en base avec SQL*Loader

Les images des articles sont directement insérées dans la colonne image de la table Article. Cette colonne est au format BLOB (Binary Large Object).

L’insertion en base des images nécessitent l’utilisation de SQL*Loader qui est un outil dans Oracle. Il est disponible en ligne de commande avec la commande sqlldr. Il permet de charger des données dans des tables à partir de fichiers texte externes.

Dans le répertoire K:\ENI\OuvrageEJB3\Oracle par exemple, créez le fichier externe articles.dat avec le contenu suivant :

0; Chaussures; 12; 0; 0;K:\ENI\OuvrageEJB3\Oracle\images\chaussures.jpg

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmGXN8BnH82ICwA=-enidentnumber

Page 408: Ejb Jsf Struts Flex Jasper

1; Ballon; 13; 0; 1;K:\ENI\OuvrageEJB3\Oracle\images\ballonfoot.jpg 2; Tablette chocolat noir; 7; 1; 2;K:\ENI\OuvrageEJB3\Oracle\images\chocolatNoir.jpg 3; Tablette chocolat blanc; 3; 1; 3;K:\ENI\OuvrageEJB3\Oracle\images\chocolatBlanc.jpg 4; Java pour les nuls; 22; 2; 4;K:\ENI\OuvrageEJB3\Oracle\images\JavaNuls.jpg 5; Java pour les pros; 32; 2; 5;K:\ENI\OuvrageEJB3\Oracle\images\JavaPros.jpg 6; Photoshop pour les nuls; 28; 3; 6;K:\ENI\OuvrageEJB3\Oracle\images\PhotoshopNuls.jpg 7; Photoshop pour les pros; 38; 3; 7;K:\ENI\OuvrageEJB3\Oracle\images\PhotoshopPros.jpg 8; Marteau; 8; 4; 8;K:\ENI\OuvrageEJB3\Oracle\images\marteau.jpg 9; Clou; 1; 4; 9;K:\ENI\OuvrageEJB3\Oracle\images\clou.jpg 10; Brouette; 10; 5; 10;K:\ENI\OuvrageEJB3\Oracle\images\brouette.jpg 11; Pioche; 11; 5; 11;K:\ENI\OuvrageEJB3\Oracle\images\pioche.jpg 12; Pelle; 23; 5; 12;K:\ENI\OuvrageEJB3\Oracle\images\pelle.jpg 13; Truelle; 13; 5; 13;K:\ENI\OuvrageEJB3\Oracle\images\truelle.jpg

Chaque ligne du fichier contient les données d’un article (id, nom, prix, id produit, id stock, image). Les données sont séparées par un point­virgule. Ce fichier suppose que les images soient stockées dans le répertoire K:\ENI\OuvrageEJB3\Oracle\images.

Un fichier de contrôle est nécessaire pour indiquer à SQL*Loader où se trouvent les données (article.dat), comment lire et parser les données, et où les insérer. Le fichier de contrôle sqlLoader_images.ctl est le suivant :

LOAD DATA INFILE ’articles.dat’ REPLACE INTO TABLE ARTICLE FIELDS TERMINATED BY ’;’ ( ARTICLEID , NOM , PRIX , PRODUIT_FK , STOCK_FK , fname filler, image lobfile(fname) TERMINATED BY EOF )

Une fois le fichier des données et le fichier de contrôle créés, il ne reste plus qu’à lancer la commande sqlldr depuis une fenêtre DOS :

K:\ENI\OuvrageEJB3\Oracle>sqlldr userid=hr/toto control=sqlLoader_images log=resultat.log

Si l’insertion s’est déroulé correctement, le message suivant s’affiche à la suite :

Des messages détaillés sont affichés dans le fichier de log resultat.log spécifié en paramètre de la commande, à l’aide de l’attribut log. Les données insérées dans la table Article sont ensuite consultables avec l’outil Toad, par exemple :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmGXN8BnH82ICwA=-enidentnumber

Page 409: Ejb Jsf Struts Flex Jasper

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MmGXN8BnH82ICwA=-enidentnumber

Page 410: Ejb Jsf Struts Flex Jasper

Le driver P6Spy

P6Spy est un driver Open Source qui se substitue au driver JDBC d’origine pour intercepter toutes les requêtes exécutées et afficher les valeurs des paramètres de ces requêtes. Il ne nécessite pas d’ajout au niveau du code. Il est donc très utile pour debugger. Voici les différentes étapes pour l’installation de cet outil très utile :

1 ­ Téléchargez le fichier p6spy­install.zip sur le site http://www.p6spy.com/download.html. Décompressez ce fichier. Il contient l’archive p6spy.jar et le fichier spy.properties.

2 ­ Copiez l’archive p6spy.jar dans le répertoire lib de JBoss : K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\lib

3 ­ Copiez le fichier spy.properties dans le répertoire conf de JBoss : K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\conf

4 ­ Éditez ce fichier spy.properties pour décommenter/commenter les lignes suivantes :

# oracle driver realdriver=oracle.jdbc.driver.OracleDriver # the mysql open source driver # realdriver=org.gjt.mm.mysql.Driver ... logfile=K:/ENI/DeveloppementEJB3/jboss- 6.0.0.M1/server/default/log/spy.log

Les requêtes seront désormais affichées dans le fichier spy.log.

5 ­ Enfin, éditez le fichier de configuration de la datasource Oracle oracle­ds.xml qui se trouve dans K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\server\default\deploy et substituez la classe du driver Oracle par celle de P6Spy :

<driver-class>com.p6spy.engine.spy.P6SpyDriver</driver-class> <!--<driver-class>oracle.jdbc.driver.OracleDriver</driver-class> -->

Il est nécessaire de redémarrer le serveur pour que ces modifications soient prises en compte.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MiuNhMpnH82ICwA=-enidentnumber

Page 411: Ejb Jsf Struts Flex Jasper

Le serveur LDAP

1. Installation d’Apache Directory Studio

Apache Directory Studio est un browser LDAP gratuit, basé sur Eclipse et qui inclut le serveur LDAP Open Source ApacheDS. Vous pouvez le récupérer sur le site officiel http://directory.apache.org/studio/downloads.html. Il contient un éditeur de fichier LDIF avec une coloration syntaxique ainsi que l’affichage de sa structure.

Apache Directory Studio existe en versions plugin et standalone.

La version plugin peut être récupérée et installée à travers Eclipse. Pour cela allez dans le menu puis Help ­ Install New Software et entrez le site suivant : http://directory.apache.org/studio/update/1.x.

La version standalone est téléchargeable sous la forme d’un fichier exécutable nommé ApacheDirectoryStudio­win32­1.5.2.v20091211.exe (environ 92.6 MB). Il ne reste plus qu’à lancer cet exécutable et à suivre les instructions pour l’installation.

2. Configuration du serveur

Pour ajouter un serveur depuis la version standalone ou la version plugin, allez dans la vue Serveurs et cliquez sur le bouton Nouveau Serveur, puis entrez un nom pour le serveur, par exemple localhost :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqZRwtVnH82ICwA=-enidentnumber

Page 412: Ejb Jsf Struts Flex Jasper

Une nouvelle instance du serveur est alors créée. Les paramètres du serveur peuvent être affichés en double cliquant sur le serveur pour faire apparaître le contenu du fichier server.xml. Le port d’écoute par défaut du serveur LDAP est 10389 et le port d’écoute par défaut du serveur LDAPS est 10686.

Les login et mot de passe par défaut sont uid=admin,ou=system et secret, respectivement. Le démarrage du serveur s’effectue en faisant un clic droit sur le nom du serveur puis en sélectionnant Démarrer dans le menu contextuel.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MqZRwtVnH82ICwA=-enidentnumber

Page 413: Ejb Jsf Struts Flex Jasper

L’environnement Flex

1. Installation de Flex Builder

Pour faire du développement Flex, il existe trois façons :

Installer la version standalone de l’IDE Flex Builder.

Installer la version plugin de Flex Builder dans Eclipse.

Installer le Flex SDK (Open Source).

Eclipse étant déjà installé, il est préférable d’opter pour la version plugin de l’IDE Flex Builder. La version plugin de Flex Builder est valable 60 jours (tout comme la version standalone).

Depuis le site d’Adobe, récupérer la version plugin à l’adresse suivante :

http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3email.

Il s’agit d’un exécutable d’environ 330 MB : FB3_WWEJ_Plugin.exe.

Eclipse doit être fermé pour pouvoir installer le plugin. Lancez­le et suivez la procédure d’installation, illustrée par les écrans suivants.

Commencez par choisir la langue :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrEYF+pnH82ICwA=-enidentnumber

Page 414: Ejb Jsf Struts Flex Jasper

Puis acceptez les termes de la licence :

Installez le plugin dans le répertoire K:\ENI\DeveloppementEJB3\PluginFlex, par exemple.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrEYF+pnH82ICwA=-enidentnumber

Page 415: Ejb Jsf Struts Flex Jasper

Spécifiez le répertoire d’Eclipse : K:\ENI\WORK\eclipse­jee­galileo­SR1­win32.

Installez Flash Player dans les navigateurs de votre choix et cliquez sur Next :

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrEYF+pnH82ICwA=-enidentnumber

Page 416: Ejb Jsf Struts Flex Jasper

Vous pouvez ensuite revoir la configuration de l’installation avant son lancement.

Cliquez sur le bouton Install si les paramètres affichés vous conviennent :

L’installation commence alors :

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrEYF+pnH82ICwA=-enidentnumber

Page 417: Ejb Jsf Struts Flex Jasper

La phase finale de l’installation consiste à spécifier le numéro de série fourni lors de l’achat d’une licence. Par défaut, l’option cochée est la version d’évaluation, valable pendant 60 jours :

- 5 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrEYF+pnH82ICwA=-enidentnumber

Page 418: Ejb Jsf Struts Flex Jasper

Un fichier nommé com.adobe.flexbuilder.feature.core.link est créé dans le répertoire K:\ENI\WORK\eclipse­jee­galileo­SR1­win32\links\. Il indique le chemin du plugin Flex installé. Pour des raisons de compatibilité

avec Galileo, la dernière version d’Eclipse, veillez à ce que ce chemin soit précédé du mot­clé path. La ligne présente dans ce fichier doit donc être du type : path=K:/ENI/DeveloppementEJB3/PluginFlex.

2. Installation de Granite Data Services

GraniteDS est une solution alternative à Adobe LiveCycle DataServices et à BlazeDS. Elle supporte totalement JPA, ce qui n’est pas le cas de BlazeDS qui n’a pas de support intégré du lazy­loading par exemple.

Installation de la distribution :

La distribution peut être téléchargée sur le site officiel de GraniteDS à l’adresse http://www.graniteds.org/.

Récupérez le fichier compressé graniteds­2.1.0.RC2.zip (environ 60 MB) et décompressez­le. Dans la version décompressée de cette distribution, à l’intérieur du répertoire graniteds/examples/graniteds_ejb3, vous pouvez trouver un exemple d’utilisation des EJB avec GraniteDS. Les librairies faisant partie de cette distribution et à installer dans le projet sont mentionnées dans le chapitre Développement d’un client avec Flex 3 ­ Ajout des librairies et des fichiers de configuration.

Installation du plugin Granite Eclipse Builder (Gas3) :

Ce plugin permet la génération de code ActionScript à partir de classes Java. Récupérez la librairie org.granite.builder_2.1.0.RC2.jar sur le site de GraniteDS à l’URL suivante : http://sourceforge.net/projects/granite/files/. Cette librairie a une taille d’environ 5 MB. Copiez­la vers le répertoire plugins d’Eclipse et redémarrez Eclipse pour que le plugin apparaisse.

Pour d’avantage d’informations et obtenir de l’aide, un groupe Yahoo existe à l’adresse suivante : http://tech.groups.yahoo.com/group/graniteds/

- 6 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MrEYF+pnH82ICwA=-enidentnumber

Page 419: Ejb Jsf Struts Flex Jasper

Session bean sans interfaces

1. Vue du bean sans interface

Un session bean peut implémenter et exposer une interface locale ou une interface distante, ou les deux. Une interface représente la vue du bean pour le client. Depuis la version 3.1, ces interfaces sont maintenant optionnelles pour les session beans. Cette fonctionnalité est disponible dans JBoss depuis la version 6.0.0.M2 qui est sortie en février 2010.

Concrètement le code d’un session bean peut être réduit à une simple classe. Par exemple, le stateless session bean ContactServiceBean suivant n’implémente pas d’interface :

package com.eni.dvtejb31.ejb.sessionbeans; @Stateless @TransactionAttribute(value = TransactionAttributeType.REQUIRED) public class ContactServiceBean ...

Le bean possède donc, de façon implicite, une vue sans interface car il n’implémente pas d’interfaces. C’est le comportement par défaut. Si le bean implémente une interface alors le bean n’a pas de vue sans interface.

Le développement d’un stateless session bean est donc devenu aussi simple que le développement d’un POJO(Plain Old Java Object). Ce stateless session bean utilisé pour la gestion des contacts bénéficie évidemment de tous les services inhérents à un container EJB (sécurité, gestion des transactions, injection de dépendance, interceptors, cycles de vie, etc.).

2. L’annotation @javax.ejb.LocalBean

Cette annotation permet à un bean d’obtenir une référence à partir de sa vue sans interface, dans le cas où le bean implémente une ou plusieurs interfaces (locale, distante ou autre). Elle ajoute donc une vue sans interface à un bean qui implémente des interfaces.

En considérant l’interface distante IcontactServiceRemote.java suivante :

package com.eni.dvtejb31.ejb.sessionbeans; import com.eni.dvtejb31.ejb.entitybeans.Contact; public interface IContactServiceRemote public String renvoyerContacts(); public Contact updater(Contact contact); public Contact ajouter(); public void supprimer(Contact contact); public Contact rechercher(Integer id);

Le stateful session bean ContactServiceBean.java qui implémente cette interface doit utiliser l’annotation @LocalBean pour ajouter de façon explicite une vue sans interface :

@Stateless @TransactionAttribute(value = TransactionAttributeType.REQUIRED) @Remote(IContactServiceRemote.class) @LocalBean public class ContactServiceBean implements IContactServiceRemote ...

Cette annotation rend alors encore possible l’injection du bean à partir de sa vue sans interface. L’injection est faite dans le managed bean ContactBean.java, qui est donc le client du bean :

package com.eni.dvtejb31.managedbeans;

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MndTL/dnH82ICwA=-enidentnumber

Page 420: Ejb Jsf Struts Flex Jasper

import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; Import com.eni.dvtejb31.ejb.entitybeans.Contact; import com.eni.dvtejb31.ejb.sessionbeans.ContactServiceBean; @ManagedBean @SessionScoped public class ContactBean private final static String LISTE_CONTACTS = "listeContacts.xhtml"; private final static String MODIFIER_CONTACT = "modifierContact.xhtml";

@EJB

private ContactServiceBean contactService;

private DataModel<Contact> contacts; private Contact contactCourant; @PostConstruct public void init() rafraichir(); public DataModel<Contact> getContacts() return contacts; public void rafraichir() contacts = new ListDataModel<Contact>(); contacts.setWrappedData(contactService.findContacts()); public String ajouter() contactCourant = contactService.ajouter(); return MODIFIER_CONTACT; public String supprimer() Contact contact = contacts.getRowData(); contactService.supprimer(contact); rafraichir(); return LISTE_CONTACTS; public String modifier() contactCourant = contacts.getRowData(); rafraichir(); return MODIFIER_CONTACT; public String sauver() contactService.updater(contactCourant); rafraichir(); return LISTE_CONTACTS; public String annuler() rafraichir(); return LISTE_CONTACTS; public Contact getContactCourant() return contactCourant; public void setContactCourant(Contact contactCourant)

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MndTL/dnH82ICwA=-enidentnumber

Page 421: Ejb Jsf Struts Flex Jasper

this.contactCourant = contactCourant;

3. Fichier de persistance

Dans un projet Web, le fichier persistence.xml doit se trouver sous le répertoire /META­INF. Créez le répertoire /classes sous le répertoire /WEB­INF. Créez ensuite le répertoire /META­INF sous le répertoire /classes. Ajoutez le fichier de persistence.xml suivant :

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.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_1_0.xsd"> <persistence-unit name="CarnetContactsJavaEE" transaction- type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/OracleDS</jta-data-source> <class>com.eni.dvtejb31.ejb.entitybeans.Contact</class> <properties> <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/> <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MndTL/dnH82ICwA=-enidentnumber

Page 422: Ejb Jsf Struts Flex Jasper

Simplification du packaging

Le déploiement est rendu plus facile puisqu’il est désormais possible de packager les EJB dans une archive WAR, au lieu d’une librairie JAR packagée dans une archive EAR. L’application Web de gestion des contacts est donc créée dans un projet Web dynamique avec la structure suivante :

Au final une archive CarnetContactsEJB31.war est générée. Son contenu est le suivant :

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MuMglAFoH82ICwA=-enidentnumber

Page 423: Ejb Jsf Struts Flex Jasper

Les EJB sont packagés dans le répertoire WEB­INF/classes à l’intérieur de l’archive WAR. Ils peuvent aussi être packagés dans une librairie JAR à l’intérieur du répertoire WEB­INF/lib et être déclarés dans le fichier WEB­INF/ejb­jar.xml.

Le schéma ci­après compare les packagings possibles d’EJB entre la version 3.0 et la version 3.1 :

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MuMglAFoH82ICwA=-enidentnumber

Page 424: Ejb Jsf Struts Flex Jasper

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MuMglAFoH82ICwA=-enidentnumber

Page 425: Ejb Jsf Struts Flex Jasper

Le singleton bean et la concurrence d’accès

1. Le singleton bean

Avec le nouveau type de bean appelé singleton il est possible de partager une instance unique d’un session bean au niveau de l’application. Le bean est donc créé une seule fois. Un exemple d’utilisation d’un singleton bean peut être la gestion d’un cache pour partager des données au niveau de l’application. Plusieurs annotations ont été développées autour du design pattern GOF Singleton :

Cette annotation est spécifiée au niveau de la classe que l’on souhaite définir comme singleton.

package com.eni.dvtejb31.singletons; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.Singleton; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import org.apache.log4j.Logger; import com.eni.dvtejb31.ejb.entitybeans.Contact; @Singleton public class SingletonContactBean @PersistenceContext(unitName = "CarnetContactsJavaEE", type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; private Contact contactPrincipal; @PostConstruct private void init() contactPrincipal = entityManager.find(Contact.class, 1); @PreDestroy private void destroy() entityManager.merge(contactPrincipal); public void setContactPrincipal(Contact contact) this.contactPrincipal = contact; public Contact getContactPrincipal() return contactPrincipal;

Ce singleton bean permet de récupérer un contact principal dès l’instanciation du bean et de le partager au niveau de l’application.

Cette annotation sert à indiquer au container d’instancier un session bean dès le déploiement de l’application et non pas lorsque le client invoque ce session bean pour la première fois. Cela permet de réduire le temps d’attente du client qui invoque cet EJB.

@Singleton @Startup public class SingletonBean

@javax.ejb.Singleton

@javax.ejb.Startup

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhDA4whoH82ICwA=-enidentnumber

Page 426: Ejb Jsf Struts Flex Jasper

private static final Logger log = Logger.getLogger(SingletonBean.class); @PostConstruct public void init() log.info("Initilisation"); ... ...

La méthode init(), annotée avec l’annotation @PostConstruct, est appelée immédiatement après l’instanciation du bean.

Si plusieurs beans de type singleton sont définis dans l’application, leur ordre d’instanciation peut être important. Par exemple, dans le cas où les données d’un singleton SingletonBeanA proviennent des données d’autres singletons (SingletonBeanB et SingletonBeanC) :

@Singleton public class SingletonBeanB ... @Singleton public class SingletonBeanC ... // L’instancation du bean SingletonBeanA est faite après les instanciations // des beans SingletonBeanB et SingletonBeanC @DependsOn("SingletonBeanB ", "SingletonBeanC") @Singleton public class SingletonBeanA ...

L’annotation @DependsOn spécifie donc les singletons qui doivent être instanciés avant le singleton sur lequel porte l’annotation.

Le cycle de vie du Singleton bean est similaire à celui du stateless session bean puisqu’il n’a que deux états : n’existe pas et prêt (pool de méthodes prêtes). Au début du cycle de vie, les injections de dépendance sont effectuées puis la méthode annotée @PostConstruct est exécutée. Les méthodes métiers du bean sont ensuite invoquées par le client. Enfin, la méthode annotée avec @PreDestroy() est invoquée en fin de cycle de vie, quand l’application est arrêtée.

@javax.ejb.DependsOn

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhDA4whoH82ICwA=-enidentnumber

Page 427: Ejb Jsf Struts Flex Jasper

2. La concurrence d’accès

Un session bean singleton est capable de gérer la concurrence d’accès, c’est­à­dire le cas où plusieurs clients souhaitent accéder à une instance unique du bean en même temps. Cette concurrence d’accès peut être gérée au niveau du container ou au niveau du bean. L’annotation @javax.ejb.ConcurrencyManagement permet de spécifier ce niveau en paramètre :

@ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONTAINER) : la concurrence d’accès du singleton est gérée par le container. C’est la gestion par défaut.

@ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.BEAN) : la concurrence d’accès du singleton est gérée par le bean, donc par le développeur.

Dans une concurrence d’accès gérée par le container, l’annotation @javax.ejb.Lock est utilisée pour pouvoir verrouiller l’accès aux méthodes d’un bean. Elle s’applique au niveau de la classe ou d’une méthode et prend en paramètre le type de verrouillage :

javax.ejb.LockType.READ (verrou partagé) : la méthode du singleton bean peut être accédée par plusieurs clients simultanément.

javax.ejb.LockType.WRITE (verrou exclusif) : toutes les méthodes du singleton bean deviennent inaccessibles aux autres clients. C’est le type par défaut.

Le singleton bean suivant interdit, à tous les autres clients, l’accès à toutes ses méthodes dès qu’un client utilise la méthode incrementer() pour empêcher que plusieurs clients ne modifient en même temps la variable compteur. Ce n’est pas le cas de la méthode getCompteur() qui peut être accédée par plusieurs clients au même moment. La méthode incrementer() sera de nouveau accessible aux autres clients dès que le premier client aura terminé l’appel de cette méthode et à condition que sa durée d’attente (5 minutes), spécifiée à l’aide de l’annotation @javax.ejb.AccessTimeout, n’ai pas expiré. Cette annotation spécifie la durée maximum, par défaut en millisecondes, pendant laquelle un client peut attendre pour accéder à une méthode. Elle représente donc un délai maximum d’attente qui, lorsqu’il expire, lève une exception de type javax.ejb.ConcurrentAccessTimeoutException. L’exception signifie alors que le client n’a pas pu invoquer la méthode pendant le délai maximum d’attente. Cette annotation peut s’appliquer au niveau d’une méthode ou au niveau d’une classe.

import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementType; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.Singleton; import javax.ejb.AccessTimeout; @Singleton @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) public class SingletonA private int compteur; @Lock(LockType.READ) public int getCompteur() return compteur; @Lock(LockType.WRITE) @AccessTimeout(300000) // 5 mn public void incrementer() compteur++;

Dans une concurrence d’accès gérée par le bean, c’est au développeur de s’assurer qu’il n’y a pas de conflits. Par exemple avec l’utilisation d’un bloc synchronized ou avec le package java.util.concurrent.atomic, disponible depuis la version 5 de Java SE, comme dans l’exemple de singleton bean suivant :

import java.util.concurrent.atomic.AtomicInteger; import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementT ype; import javax.ejb.Singleton;

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhDA4whoH82ICwA=-enidentnumber

Page 428: Ejb Jsf Struts Flex Jasper

@Singleton @ConcurrencyManagement(ConcurrencyManagementType.BEAN) public class SingletonB private AtomicInteger compteur; public int getCompteur() return compteur.get(); public void incrementer() compteur.incrementAndGet();

- 4 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MhDA4whoH82ICwA=-enidentnumber

Page 429: Ejb Jsf Struts Flex Jasper

Appels asynchrones avec les session beans

1. L’annotation @Asynchronous et l’interface Future

Un appel asynchrone d’une méthode d’un session bean (stateful, stateless ou singleton) permet de retourner le contrôle au client immédiatement, sans qu’il ait à attendre le traitement de cette méthode. Les EJB de type MDB permettent déjà d’effectuer des appels asynchrones (cf. chapitre Utilisation des Web Services). Cependant la version 3.1 des EJB rend possible ces appels sans passer par des MDBs et d’avoir à utiliser JMS.

L’annotation @javax.ejb.Asynchronous expose une méthode de session bean de façon asynchrone. Cette annotation s’applique au niveau d’une méthode ou d’une classe/interface. Dans ce dernier cas, toutes les méthodes de la classe/interface deviennent asynchrones. Les méthodes ou classes/interfaces asynchrones peuvent également être déclarées via le descripteur de déploiement.

Une méthode asynchrone a un type de retour qui est soit void soit java.util.concurrent.Future<V>, où V est le type de la valeur de retour. Le type de retour Future<V> rend la valeur de retour d’une méthode asynchrone disponible pour un client.

Si le type de retour est void, alors la méthode ne doit pas déclarer des application exceptions. En revanche, ces exceptions sont possibles si le type de retour est Future<V>.

L’interface java.util.concurrent.Future fournit des méthodes pour vérifier si le traitement est fini (isDone()), terminer le traitement (cancel()) ou récupérer le résultat (get()).

La classe javax.ejb.AsyncResult est une implémentation de cette interface Future. Dans l’exemple suivant, le session bean SessionBeanAsynchrone contient la méthode ajouterContact(Contact contact) pour persister en base un contact de façon asynchrone et la méthode creerContact qui crée un contact de façon asynchrone et renvoie un résultat.

package com.eni.dvtejb31.ejb.sessionbeans; import java.util.concurrent.Future; import javax.ejb.AsyncResult; import javax.ejb.Asynchronous; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import com.eni.dvtejb31.ejb.entitybeans.Contact; @LocalBean @Stateless public class SessionBeanAsynchrone @PersistenceContext(unitName = "CarnetContactsJavaEE", type = PersistenceContextType.TRANSACTION) private EntityManager entityManager; @Asynchronous public void ajouterContact(Contact contact) entityManager.persist(contact); @Asynchronous public Future<Long> creerContact() Contact c = new Contact(); c.setNom("nomSynch"); c.setPrenom("prenomSynch"); entityManager.persist(c); entityManager.flush(); entityManager.refresh(c); return new AsyncResult<Long>(c.getContactid());

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Miw58RFoH82ICwA=-enidentnumber

Page 430: Ejb Jsf Struts Flex Jasper

Lorsqu’un client appelle une des méthodes asynchrones de ce session bean, le container redonne immédiatement la main au client et continue l’appel de cette méthode asynchrone dans un autre thread.

2. Le contexte transactionnel

Le contexte transactionnel du client n’est pas propagé lors de l’exécution d’une méthode asynchrone.

C’est un comportement logique dans la mesure où les appels asynchrones de méthodes ont pour but d’accélérer les traitements. Ils doivent donc être détachés de toute transaction existante.

Les cas de propagation transactionnelle suivants peuvent alors être considérés :

Si la méthode appelante du client est exécutée avec un mode de propagation transactionnelle REQUIRED, la méthode asynchrone appelée s’exécutera dans un mode de propagation transactionnelle équivalent à REQUIRED_NEW, donc avec une nouvelle transaction.

Si la méthode appelante du client est exécutée avec un mode de propagation transactionnelle SUPPORTS, la méthode asynchrone appelée s’exécutera en dehors de toute transaction.

Si la méthode appelante du client est exécutée avec un mode de propagation transactionnelle MANDATORY, la méthode asynchrone appelée lancera une exception de type TransactionRequiredException.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Miw58RFoH82ICwA=-enidentnumber

Page 431: Ejb Jsf Struts Flex Jasper

Le service Timer EJB

Il existe désormais deux façons de créer un service Timer :

De façon déclarative, avec les annotations ou le descripteur de déploiement. C’est une nouveauté des EJB 3.1.

De façon programmatique, avec l’interface TimerService qui existe depuis la version 2.1.

1. Service Timer avec l’annotation @Schedule

Le service Timer des EJB possède maintenant une syntaxe proche des crons Unix, ce qui permet de mettre en place des tâches à des fréquences ou des dates plus précises. Par exemple, sous Unix, l’entrée de la table crontab suivante lance le script de sauvegarde st_bkup tous les mardis, mercredis, vendredis et samedis à 4h00 du matin :

0 4 * * 2,3,5,6 /usr/local/bin/st_bkup -kor -i /product >>/tmp/Backup.log 2>&

La nouvelle annotation @javax.ejb.Schedule rend possible cette précision avec divers attributs et une syntaxe de valeurs de ces attributs qui peuvent répondre à la plupart des besoins d’ordonnancement. Chaque valeur d’un attribut peut être exprimée sous forme incrémentale, de liste, avec une valeur unique, une rangée de valeurs ou un astérisque pour désigner n’importe quelle valeur :

Par exemple, le stateless session bean SessionBeanScheduler suivant définit une méthode execute() qui effectue une recherche des contacts tous les jours, du lundi au vendredi, à 10h00 du matin :

package com.eni.dvtejb31.ejb.sessionbeans; import java.util.ArrayList; import java.util.List; import javax.ejb.Schedule; import javax.ejb.Stateless; import javax.inject.Inject; import com.eni.dvtejb31.ejb.entitybeans.Contact; @Stateless public class SessionBeanScheduler List<Contact> listeContacts = new ArrayList<Contact>() ; // Injection CDI @Inject private ContactServiceBean contactService; @Schedule(dayOfWeek= "Mon-Fri", hour= "10", minute = "00") public void execute()

Attributs de @Schedule Exemples de syntaxe Explications

second "*/30" (incrémental) Toutes les 30 secondes

minute "*/10" (incrémental) Toutes les 10 minutes

hour "*" (astérisque) Toutes les heures

dayOfMonth "25" (valeur unique) Le 25ème jour du mois

month "Feb, Jul, Nov" (liste) En février, juillet et novembre

dayOfWeek "Mon­Fri" (rangée de valeurs) Du lundi au vendredi

year "2010" (valeur unique) Dans l’année 2010

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlA/jxxoH82ICwA=-enidentnumber

Page 432: Ejb Jsf Struts Flex Jasper

listeContacts = contactService.findContacts();

Ce timer est créé automatiquement par le container au moment du déploiement de l’application. Les timers ne peuvent être appliqués aux stateful session beans. Ils peuvent être appliqués aux stateless session beans, aux MDBs et aux singletons. Enfin plusieurs timers peuvent être appliqués à une même méthode grâce à l’annotation @javax.ejb.Schedules :

@Schedules( @Schedule(dayOfWeek= "Mon-Fri", hour= "10", minute = "00"), @Schedule(dayOfWeek= "*", hour="20") ) public void executeBis() listeContacts = contactService.findContacts();

La méthode executeBis() sera exécutée du lundi au vendredi, à 10h00, et tous les jours, à 20h00.

2. Service Timer avec les classes ScheduleExpression et TimerService

Pour configurer les services Timers de façon programmatique, la classe javax.ejb.ScheduleExpression est utilisée pour créer une expression calendaire et l’interface javax.ejb.TimerService pour créer un service Timer à partir de cette expression calendaire.

Le stateless session bean SessionBeanSchedulerExpr suivant est l’équivalent programmatique du session bean SessionBeanScheduler précédent :

package com.eni.dvtejb31.ejb.sessionbeans; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.ejb.ScheduleExpression; import javax.ejb.Stateless; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.TimerService; import javax.inject.Inject; import com.eni.dvtejb31.ejb.entitybeans.Contact; @Stateless public class SessionBeanSchedulerExpr List<Contact> listeContacts = new ArrayList<Contact>() ; // Injection CDI @Inject private ContactServiceBean contactService; @Resource TimerService timerService; @PostConstruct public void init() ScheduleExpression expr = new ScheduleExpression().dayOfWeek("1,5").hour(10).minute(0); Timer timer = timerService.createCalendarTimer(expr); @Timeout public void execute() listeContacts = contactService.findContacts();

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlA/jxxoH82ICwA=-enidentnumber

Page 433: Ejb Jsf Struts Flex Jasper

L’interface TimerService est injectée avec l’annotation @Resource. L’expression calendaire est passée en paramètre de la méthode createCalendarTimer(ScheduleExpression schedule). La méthode execute(), annotée avec l’annotation @Timeout, est donc exécutée à chaque fois que le timer définit dans la méthode init() expire.

- 3 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MlA/jxxoH82ICwA=-enidentnumber

Page 434: Ejb Jsf Struts Flex Jasper

Un nommage JNDI standardisé

1. Nommage JNDI avant la standardisation

Même si l’introduction de l’injection de dépendance réduit le besoin de faire un lookup dans le service de nommage JNDI (Java Naming and Directory Interface), il existe des cas où celui­ci reste nécessaire comme par exemple le lookup d’un EJB depuis un client Java SE. Dans la version 3.1 des EJB, le mapping JNDI devient un standard. Ceci facilite le portage d’EJB développés dans un serveur d’application vers un autre serveur d’application car les noms JNDI étaient jusqu’à présent spécifiques à chaque serveur d’application. Ainsi, le nommage JNDI utilisé dans le serveur JBoss est différent du nommage JNDI utilisé dans le serveur GlassFish.

Par exemple, dans JBoss, le lookup d’une interface remote du stateful session bean PanierBean s’effectue de la façon suivante :

Extrait du stateful session bean PanierBean.java :

package com.eni.dvtejb.metier.sessions; @Stateful @Remote (PanierBeanRemote.class) public class PanierBean implements PanierBeanRemote ...

Lookup depuis un client :

InitialContext initialContext = new InitialContext(); PanierBeanRemote panier = (PanierBeanRemote) initialContext.lookup("VenteEnLigne/PanierBean/remote");

Ce lookup respecte la norme JBoss suivante : <nomArchiveEAR>/<nomEJB>/remote.

Dans le serveur GlassFish, ce lookup s’effectue différemment puisque la méthode peut prendre en paramètre le nom associé dans l’attribut mappedName ou bien le nom complet de l’interface remote :

InitialContext iC = new InitialContext(); PanierBeanRemote panier = (PanierBeanRemote) iC.lookup("com.eni.dvtejb.metier.sessions.PanierBeanRemote");

2. Nommage JNDI après la standardisation

Trois namespaces (espaces de nommage) sont disponibles : java:global, java:module et java:app. Désormais le mapping JNDI standard impose au container la syntaxe générale suivante :

ajava:global[/<nomArchiveEAR>]/<nomModule>/<nomBean>[!<nomCompletInterface>]

où :

java:global est le namespace

<nomArchiveEAR> désigne le nom de l’archive EAR si l’application est packagée dans une archive EAR.

<nomModule> : si l’application est packagée dans une archive EAR, le nom du module est le nom de l’archive WAR ou JAR dans laquelle est packagé l’EJB (session bean). Dans le cas d’une application WAR, le nom du module est le nom de cette archive.

<nomBean> désigne le nom de l’EJB, non qualifié, c’est­à­dire sans le nom du package.

Les valeurs entre crochets sont donc optionnelles et dépendent du type d’archive. Si l’EJB expose une seule vue au client (en implémentant aucune ou une seule interface) alors le container doit également créer une entrée de nom JNDI qui respecte la syntaxe suivante:

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mi6tzyRoH82ICwA=-enidentnumber

Page 435: Ejb Jsf Struts Flex Jasper

java:global[/<nomArchiveEAR>]/<nomModule>/<nomBean>

L’archive VenteEnLigne.ear contient l’archive VenteEnLigneEJB.jar qui contient les classes EJB.

Donc les deux syntaxes peuvent être appliquées au client du stateful session bean PanierBean puisqu’il n’implémente qu’une seule interface. Les entrées des noms JNDI sont alors les suivantes :

iC.lookup("java:global/VenteEnLigne/VenteEnLigneEJB/PanierBean"); iC.lookup("java:global/VenteEnLigne/VenteEnLigneEJB/PanierBean!com.e ni.dvtejb.metier.sessions.PanierRemote");

Le container doit également enregistrer un EJB dans un namespace d’application (java:app) et un namespace de module (java:module). Deux autres syntaxes sont donc imposées :

java:app[/<nomModule>]/<nomBean>[!<nomCompletInterface>] java:module[/<nomBean>]/[!<nomCompletInterface>]

Les namespaces java:app et java:module sont utilisés par les clients qui s’exécutent dans la même application et le même module que le bean, respectivement.

Il en résulte les quatre autres entrées de noms JNDI possibles pour le stateful session bean PanierBean.java :

iC.lookup("java:app/VenteEnLigneEJB/PanierBean"); iC.lookup("java:app/VenteEnLigneEJB/PanierBean!com.eni.dvtejb.metier .session s.PanierRemote"); iC.lookup("java:module/PanierBean"); iC.lookup("java:module/PanierBean!com.eni.dvtejb.metier.sessions.Pan ierRemote");

Cette standardisation du nommage JNDI se retrouve également au niveau de l’injection, avec l’annotation @EJB.

- 2 - © ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mi6tzyRoH82ICwA=-enidentnumber

Page 436: Ejb Jsf Struts Flex Jasper

EJB Lite et les profils

1. EJB Lite

Certaines applications ne requièrent pas tous les services qui font partie des EJB. Un sous­ensemble des EJB, EJB 3.1 Lite (Léger), a donc été introduit. Il peut fonctionner en dehors d’un serveur d’application.

EJB 3.1 Lite inclut au minimum les services suivants :

Stateless, stateful session beans et singletons. Avec des vues locales ou sans interface. Les invocations de méthodes sont synchrones seulement.

Transactions gérées par le container et transactions gérées par le bean.

Sécurité déclarative et programmatique.

Interceptors.

Descripteur de déploiement.

Java Persistence API 2.0.

2. Les profils

Cette version allégée des EJB est inclus dans le profil Web (Web profile en anglais). Le profil est un concept nouveau dans Java EE 6. Par exemple, le profil Web 1.0 correspond à un sous­ensemble des APIs Java EE qui embarque uniquement les APIs utilisées pour le développement d’applications Web telles que EJB Lite, JSF 2, Servlet 3, CDI 1.0, JPA 2, JSP 2.2, JSTL 1.2, EL 2.2, JTA 1.1, Bean Validation 1.0, Dependency Injection for Java 1.0, Managed Beans 1.0.

Le Full profile contient toutes les APIs. Java EE 6 permet de créer ses propres profils suivant les besoins des applications.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MnT/8StoH82ICwA=-enidentnumber

Page 437: Ejb Jsf Struts Flex Jasper

Container EJB embarqué

Une API permet désormais d’embarquer un container EJB dans un environnement JAVA SE. Cette API est présente dans le package javax.ejb.embeddable. Le client instancie un container EJB qui tourne dans la même JVM que celle du client. La classe EJBContainer contient des méthodes pour créer et initialiser un container EJB embarqué (createEJBContainer()), récupérer le contexte de nommage (getContext()) et arrêter l’instance du container (close()).

Un container embarqué peut par exemple être utile dans le cas de tests unitaires. Le test unitaire JUnit suivant recherche un contact en passant par un container EJB 3.1 embarqué :

package com.eni.dvtejb31.ejb.tests; import static org.junit.Assert.assertNotNull; import javax.ejb.embeddable.EJBContainer; import javax.naming.Context; import javax.naming.NamingException; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import com.eni.dvtejb31.ejb.entitybeans.Contact; import com.eni.dvtejb31.ejb.sessionbeans.ContactServiceBean; public class ContactServiceBeanTest private static EJBContainer container; @BeforeClass public static void setUpClass() throws Exception container = EJBContainer.createEJBContainer(); @Test public void hello() throws NamingException Context ctx = container.getContext(); ContactServiceBean contactServiceBean = (ContactServiceBean) ctx.lookup("java:global/CarnetContactsEJB31/ContactServiceBean"); assertNotNull(contactServiceBean); Contact contact = contactServiceBean.rechercher(0); assertNotNull(contact); container.close(); @AfterClass public static void tearDownClass() throws Exception container.close();

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1Mu5DhTJoH82ICwA=-enidentnumber

Page 438: Ejb Jsf Struts Flex Jasper

Tableau récapitulatif des nouvelles annotations

Le tableau suivant résume les apports de la version 3.1 des EJB en listant les nouvelles annotations introduites et dont la plupart sont décrites dans ce chapitre :

Fonctionnalités Annotations Commentaires

Vue sans interface @LocalBean Indique que le bean expose une vue sans interface.

@Singleton Indique que le bean est un singleton.

@ConcurrencyManagement Définit le type de gestion de la concurrence : BEAN ou CONTAINER.

Singleton bean @Lock Indique au container le mécanisme de verrouillage à appliquer (WRITE OU READ).

@DependsOn Indique des dépendances entre Singleton beans.

@Startup Le conteneur initialise le Singleton bean au démarrage de l’application

@AccessTimeout Définit la durée de blocage d’une requête de session bean lorsqu’elle tente d’accéder à une instance de bean déjà utilisée par un autre session bean.

@StatefulTimeout Définit la durée pendant laquelle un stateful session bean peut rester sans recevoir de requête avant d’être supprimé par le container.

Stateful session bean @AfterBegin L’équivalent de la méthode afterBegin() de l’interface SessionSynchronization.

@AfterCompletion L’équivalent de la méthode afterCompletion() de l’interface SessionSynchronization.

@BeforeCompletion L’équivalent de la méthode BeforeCompletion() de l’interface SessionSynchronization.

@Schedule Crée un service Timer.

Timer @Schedules Crée plusieurs services Timer.

@AroundTimeout Définit une méthode Interceptor pour réagir à une méthode timeout.

- 1 -© ENI Editions - All rigths reserved - Moha Anisa

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6NDIzMzg5IC0gTW9oYSBBbmlzYSAtIGJhZDVmMjk5LWQzNDAtNGRmNi1iMDUyLTcyNDU5ZmM1NTI1MjH+uj5oH82ICwA=-enidentnumber

Page 439: Ejb Jsf Struts Flex Jasper