jboss - thomashenrywarner.free.frthomashenrywarner.free.fr/ebooks/java/jboss - développement... ·...

284
Ce livre sur JBoss s'adresse aux développeurs Java quotidiennement confrontés au développement et au déploiement d'applications distribuées, que ce soient des applications Web, des reprises de projets en EJB 2 ou de nouveaux projets en EJB 3. JBoss est devenu un serveur d'applications incontournable dans le monde Java et ce livre présente de manière pratique le déploiement et la sécurisation des applications sous JBoss, en tenant compte des différentes versions des spécifications Sun : Servlet / JSP, EJB 2 pour les projets existants et EJB 3 pour les nouveaux projets. Le livre commence par un rappel sur l'architecture JEE, puis se poursuit sur l'installation de JBoss. Les services fondamentaux de JBoss utilisés par les applications distribuées y sont présentés et expliqués. L'auteur décrit l'architecture interne de JBoss et présente les principaux services : JMX, JNDI, JMS, JTA, JCA, JAAS. Les chapitres suivants décrivent les principes de fonctionnement et de codage des composants EJB2, EJB3, servlet/JSP. Le déploiement et l'empaquetage de ces composants sous JBoss sont commentés et illustrés par des exemples qui sont disponibles en téléchargement. La gestion des transactions et de la sécurité font l'objet de chapitres à part entière. Après le développement et le déploiement... l'administration de JBoss est présentée : la console JMX, l'instanciation de plusieurs instances, la mise en cluster, configuration avec Apache mod_jk. Le dernier chapitre présente l'intégration de JBoss à Eclipse et notamment comment intégrer dans Eclipse les exemples téléchargeables. Des exemples sont en téléchargement sur cette page. 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 JBoss Développement, déploiement et sécurisation d'applications JEE Franck SIMON Résumé L'auteur Ingénieur conseil, Franck Simon exerce en tant qu'indépendant depuis 1994. Consultant et développeur sur les technologies Java/JEE, il intervient régulièrement sur des développements autour des technologies Java/JEE et la variété de ses missions dans ce domaine l'ont conduit à écrire un livre véritablement opérationnel sur JBoss. - 1 - © ENI Editions - All rigths reserved - Kaiss Tag

Upload: truongliem

Post on 10-Sep-2018

220 views

Category:

Documents


0 download

TRANSCRIPT

Ce livre sur JBoss s'adresse aux développeurs Java quotidiennement confrontés au développement et au déploiement d'applications distribuées, que ce soient des applications Web, des reprises de projets en EJB 2 ou de nouveaux projets en EJB 3. JBoss est devenu un serveur d'applications incontournable dans le monde Java et ce livre présente de manière pratique le déploiement et la sécurisation des applications sous JBoss, en tenant compte des différentes versions des spécifications Sun : Servlet / JSP, EJB 2 pour les projets existants et EJB 3 pour les nouveaux projets. Le livre commence par un rappel sur l'architecture JEE, puis se poursuit sur l'installation de JBoss. Les services fondamentaux de JBoss utilisés par les applications distribuées y sont présentés et expliqués. L'auteur décrit l'architecture interne de JBoss et présente les principaux services : JMX, JNDI, JMS, JTA, JCA, JAAS. Les chapitres suivants décrivent les principes de fonctionnement et de codage des composants EJB2, EJB3, servlet/JSP. Le déploiement et l'empaquetage de ces composants sous JBoss sont commentés et illustrés par des exemples qui sont disponibles en téléchargement. La gestion des transactions et de la sécurité font l'objet de chapitres à part entière. Après le développement et le déploiement... l'administration de JBoss est présentée : la console JMX, l'instanciation de plusieurs instances, la mise en cluster, configuration avec Apache mod_jk. Le dernier chapitre présente l'intégration de JBoss à Eclipse et notamment comment intégrer dans Eclipse les exemples téléchargeables. Des exemples sont en téléchargement sur cette page.

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

JBoss Développement, déploiement et sécurisation d'applications JEE

Franck SIMON

Résumé

L'auteur

Ingénieur conseil, Franck Simon exerce en tant qu'indépendant depuis 1994. Consultant et développeur sur les technologies Java/JEE, il intervient régulièrement sur des développements autour des technologies Java/JEE et la variété de ses missions dans ce domaine l'ont conduit à écrire un livre véritablement opérationnel sur JBoss.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMziQFsSqGRzIgLAA==-enidentnumber

Les exemples cités tout au long de cet ouvrage sont téléchargeablesà l'adresse suivante : http://www.editions-eni.fr

Saisissez la référence ENI de l'ouvrage EI4JBOS dans la zone de rechercheet validez. Cliquez sur le titre du livre puis sur le lien de téléchargement.

Chapitre 1

Introduction1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92. Architecture des applications distribuées . . . . . . . . . . . . . . . . . . . . . 10

2.1 L'invocation de méthodes distantes : RMI. . . . . . . . . . . . . . . . 143. Rappels sur XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204. Rappels sur J2EE 1.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245. Rappels sur JEE 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

5.1 Les annotations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275.2 Injection par proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6. Présentation du serveur d'applications JBoss . . . . . . . . . . . . . . . . . . 34

Chapitre 2

Installation de JBoss1. Téléchargement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

1.1 Téléchargement de JBoss. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382. Installation de JBoss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403. Structure des répertoires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

3.1 Structure du répertoire de type server . . . . . . . . . . . . . . . . . . . 434. Démarrer et arrêter le serveur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

4.1 Sous Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454.2 Sous Ubuntu. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464.3 Suivi du démarrage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464.4 Test de JBoss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494.5 Arrêt de JBoss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504.6 Arrêt et démarrage des autres configurations serveur . . . . . . . 51

4.6.1 Pour lancer le serveur minimal . . . . . . . . . . . . . . . . . . . 514.6.2 Pour lancer la configuration all . . . . . . . . . . . . . . . . . . . 52

5. Déploiement et repli des applications JEE . . . . . . . . . . . . . . . . . . . . 535.1 Exemple de déploiement d'un EJB . . . . . . . . . . . . . . . . . . . . . . 545.2 Exemple de déploiement d'une application Web . . . . . . . . . . . 575.3 Exemple de déploiement d'une application d'entreprise . . . . . . 595.4 Retrait d'une application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

Chapitre 3

Architecture de JBoss1. Noyau du serveur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

1.1 Présentation de la spécification JMX . . . . . . . . . . . . . . . . . . . . 611.1.1 La couche instrumentation : l'interface HelloMBean . . . 631.1.2 La couche instrumentation :

implémentation du MBean . . . . . . . . . . . . . . . . . . . . . . 631.1.3 La couche Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641.1.4 La couche distribuée : jconsole . . . . . . . . . . . . . . . . . . . 651.1.5 Supervision : notification d'événement . . . . . . . . . . . . . 67

1.2 Implémentation de JMX dans JBoss . . . . . . . . . . . . . . . . . . . . 691.3 Processus de démarrage de JBoss . . . . . . . . . . . . . . . . . . . . . . . 701.4 Le fichier jboss-service.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.5 Propriétés utilisables dans les fichiers de configuration JBoss. . 73

1.5.1 Propriétés de démarrage . . . . . . . . . . . . . . . . . . . . . . . . 741.5.2 Propriétés représentant des URLs ou des répertoires . . . 741.5.3 Propriétés de configuration . . . . . . . . . . . . . . . . . . . . . . 75

1.6 Connexion au serveur JMX de JBoss . . . . . . . . . . . . . . . . . . . . 751.6.1 Connexion par la console web : JMX Agent View. . . . . 751.6.2 Connexion par la console web : sécurisation . . . . . . . . . 781.6.3 Connexion par l'outil twiddle . . . . . . . . . . . . . . . . . . . . 791.6.4 Connexion par RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

2. Service de nommage JNDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832.1 Présentation de JNDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

2.1.1 Les noms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842.1.2 Les noms atomiques . . . . . . . . . . . . . . . . . . . . . . . . . . . 842.1.3 Les associations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842.1.4 Le contexte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

2.2 L'ENC (Environment Naming Context) . . . . . . . . . . . . . . . . . 882.2.1 Niveau de visibilité des contextes JNDI sous JBoss . . . . 88

JBossDéploiement et sécurisation d'applications JEE

2

2.2.2 Console de visualisation JNDI. . . . . . . . . . . . . . . . . . . . 882.3 Le service de nommage de JBoss : JBossNS . . . . . . . . . . . . . . . 92

2.3.1 Contexte de nommage standard . . . . . . . . . . . . . . . . . . 932.4 Nommage JNDI des composants déployés sous JBoss . . . . . . . 93

2.4.1 Nommage JNDI par défaut . . . . . . . . . . . . . . . . . . . . . . 942.4.2 Utilisation de jboss.xml . . . . . . . . . . . . . . . . . . . . . . . . 952.4.3 Référencement de composants avec <ejb-ref> . . . . . . . 972.4.4 Référencement dans le fichier ejb-jar.xml . . . . . . . . . . . 99

3. Services de déploiement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994. Conteneur Web. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

4.1 Fichier de configuration jboss-service.xml . . . . . . . . . . . . . . . 1024.2 Fichier de configuration server.xml . . . . . . . . . . . . . . . . . . . . 1034.3 Fichier de configuration web.xml . . . . . . . . . . . . . . . . . . . . . 1034.4 Fichier de configuration context.xml . . . . . . . . . . . . . . . . . . . 1034.5 Gestion des ressources statiques . . . . . . . . . . . . . . . . . . . . . . 104

5. Service de messagerie JMS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1045.1 Mode point à point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1055.2 Mode publication/abonnement . . . . . . . . . . . . . . . . . . . . . . . 1065.3 Spécification JMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1075.4 Modèle de programmation JMS. . . . . . . . . . . . . . . . . . . . . . . 1075.5 Les EJB orientés message . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

6. Service de transaction JTA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087. Service de connecteurs JCA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098. Service de sécurité JAAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1109. Autres services utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

9.1 Service de suivi des threads et de la mémoire . . . . . . . . . . . . 1119.2 Service Mail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1149.3 Service Log4j . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1159.4 TimerService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1169.5 Service de planification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

10. Fichiers de configuration du serveur . . . . . . . . . . . . . . . . . . . . . . . 122

Table des matières 3

Chapitre 4

Tiers de persistance1. Persistance des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

1.1 Codage du pattern DAO . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1241.2 Source de données et pool de connexion . . . . . . . . . . . . . . . . 1271.3 Autre modèles de conception de la couche de persistance . . . 129

2. Paramétrage des sources de données . . . . . . . . . . . . . . . . . . . . . . . 1302.1 Principaux éléments du fichier de configuration . . . . . . . . . . 130

2.1.1 Éléments de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1302.1.2 Principaux éléments de configuration

des sources de données . . . . . . . . . . . . . . . . . . . . . . . . 1313. Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Chapitre 5

Déploiement d'applications Web1. Introduction aux applications Web . . . . . . . . . . . . . . . . . . . . . . . . 143

1.1 Protocole HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1441.2 Architecture Web pour J2EE/JEE . . . . . . . . . . . . . . . . . . . . . . 146

2. Configuration du conteneur Web . . . . . . . . . . . . . . . . . . . . . . . . . 1482.1 Les servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1502.2 Les JSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

2.2.1 Cycle de vie d'une JSP . . . . . . . . . . . . . . . . . . . . . . . . . 1553. Les descripteurs de déploiement . . . . . . . . . . . . . . . . . . . . . . . . . . 160

3.1 Descripteur de déploiement standard web.xml . . . . . . . . . . . 1603.2 Descripteur de déploiement spécifique jboss-web.xml . . . . . . 161

3.2.1 Nom de l'application . . . . . . . . . . . . . . . . . . . . . . . . . . 1623.2.2 Association de ressources . . . . . . . . . . . . . . . . . . . . . . 162

4. Les hôtes virtuels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

JBossDéploiement et sécurisation d'applications JEE

4

Chapitre 6

Déploiement des EJB1. Configuration du conteneur d'EJB . . . . . . . . . . . . . . . . . . . . . . . . . 1712. Déploiement des EJB 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

2.1 Packaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1752.1.1 Packaging en module EJB . . . . . . . . . . . . . . . . . . . . . . 1752.1.2 Packaging dans une application d'entreprise . . . . . . . . 1762.1.3 Déploiement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

2.2 Descripteurs de déploiements . . . . . . . . . . . . . . . . . . . . . . . . 1772.2.1 Descripteur de déploiement standard ejb-jar.xml . . . . . 1772.2.2 Descripteur de déploiement spécifique jboss.xml . . . . . 1792.2.3 Descripteur de déploiement application.xml . . . . . . . . 181

2.3 Outils d'aide au codage des EJB 2 . . . . . . . . . . . . . . . . . . . . . 1832.4 EJB2 de session . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184

2.4.1 stateless . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1862.4.2 statefull. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191

2.5 EJB2 entité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1962.5.1 CMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1992.5.2 BMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214

2.6 EJB2 orienté message. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2233. Déploiement des EJB 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229

3.1 Utilisation des annotations . . . . . . . . . . . . . . . . . . . . . . . . . . 2303.2 EJB3 de session . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

3.2.1 stateless . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2313.2.2 statefull. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236

3.3 EJB3 entité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2393.3.1 Mapping avec la base de données . . . . . . . . . . . . . . . . 2393.3.2 Unité de persistance . . . . . . . . . . . . . . . . . . . . . . . . . . 2423.3.3 Attachement et détachement au contexte

de persistance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2443.3.4 Principe du chargement "paresseux". . . . . . . . . . . . . . . 248

3.4 EJB3 orienté message. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2494. Utilisation des EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252

4.1 Utilisation par une application Web . . . . . . . . . . . . . . . . . . . 2524.2 Utilisation par une application non Web. . . . . . . . . . . . . . . . 253

Table des matières 5

Chapitre 7

Gestion des transactions1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2552. Utilisation des transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2593. Modèles des transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260

3.1 Gestion programmée des transactions . . . . . . . . . . . . . . . . . . 2613.1.1 Gestion programmée des transactions en EJB 2 . . . . . . 2623.1.2 Gestion programmée des transactions en EJB 3 . . . . . . 265

3.2 Gestion déclarative des transactions . . . . . . . . . . . . . . . . . . . 2673.2.1 Gestion déclarative des transactions en EJB 2 . . . . . . . 2683.2.2 Gestion déclarative des transactions en EJB 3 . . . . . . . 271

3.3 Gestion des transactions initiées par le client . . . . . . . . . . . . 273

Chapitre 8

Gestion de la sécurité1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2752. Introduction aux techniques de chiffrement . . . . . . . . . . . . . . . . . 276

2.1 Chiffrement symétrique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2762.2 Chiffrement asymétrique . . . . . . . . . . . . . . . . . . . . . . . . . . . 2772.3 Le certificat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2792.4 Notion de clé de session . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281

2.4.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2813. Introduction à JAAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281

3.1 Les concepts clés de JAAS . . . . . . . . . . . . . . . . . . . . . . . . . . . 2823.1.1 Les classes de base de JAAS . . . . . . . . . . . . . . . . . . . . . 2823.1.2 Les classes d'authentification des JAAS . . . . . . . . . . . . 2823.1.3 Exemple d'une implémentation

d'authentification personnalisée . . . . . . . . . . . . . . . . . 2834. Le gestionnaire de sécurité JBossSX . . . . . . . . . . . . . . . . . . . . . . . . 287

4.1 LoginModule de JBoss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2874.2 Configuration du domaine de sécurité. . . . . . . . . . . . . . . . . . 288

5. Sécurisation d'une application Web . . . . . . . . . . . . . . . . . . . . . . . . 2905.1 Mise en place de HTTPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

5.1.1 Création du certificat numérique . . . . . . . . . . . . . . . . 2925.1.2 Configuration du fichier server.xml . . . . . . . . . . . . . . . 293

JBossDéploiement et sécurisation d'applications JEE

6

5.2 Définition de la méthode d'authentification . . . . . . . . . . . . . 2965.3 Définition du domaine de sécurité. . . . . . . . . . . . . . . . . . . . . 2975.4 Déclaration des contraintes . . . . . . . . . . . . . . . . . . . . . . . . . . 2975.5 Présentation de l'exemple de base . . . . . . . . . . . . . . . . . . . . . 297

5.5.1 Mise en place du domaine de sécurité de base . . . . . . . 2985.5.2 Mise en place de la méthode d'authentification . . . . . . 3005.5.3 Mise en place des contraintes de sécurité . . . . . . . . . . 3005.5.4 Déclaration des rôles utilisés . . . . . . . . . . . . . . . . . . . . 3015.5.5 Authentification de type DIGEST . . . . . . . . . . . . . . . . 3035.5.6 Méthode d'authentification de type FORM. . . . . . . . . 3065.5.7 Méthode de sécurisation par certification mutuelle . . . 308

6. Accès aux EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3216.1 Gestion des autorisations sur les EJB 2 . . . . . . . . . . . . . . . . . 3236.2 Gestion des autorisations sur les EJB 3 . . . . . . . . . . . . . . . . . 327

Chapitre 9

Configuration en mode Cluster1. Instancier plusieurs serveurs sur une même machine. . . . . . . . . . . 329

1.1 Fichier sample-bindings.xml . . . . . . . . . . . . . . . . . . . . . . . . . 3301.2 Copie d'une configuration de base . . . . . . . . . . . . . . . . . . . . . 3311.3 Changement des configurations serveurs. . . . . . . . . . . . . . . . 3311.4 Démarrage des serveurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332

2. Mise en cluster de plusieurs serveurs . . . . . . . . . . . . . . . . . . . . . . . 3332.1 La répartition de charge. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3352.2 Lancement du cluster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3352.3 Déploiement d'application. . . . . . . . . . . . . . . . . . . . . . . . . . . 337

3. Service HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3373.1 Installation du module mod_jk . . . . . . . . . . . . . . . . . . . . . . . 338

3.1.1 Pré-requis : serveur HTTP Apache. . . . . . . . . . . . . . . . 3383.1.2 Téléchargement du module . . . . . . . . . . . . . . . . . . . . . 3383.1.3 Configuration du module mod_jk . . . . . . . . . . . . . . . . 3413.1.4 Configuration des nœuds dans mod_jk . . . . . . . . . . . . 3423.1.5 Configuration de JBoss . . . . . . . . . . . . . . . . . . . . . . . . 3443.1.6 Test final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344

Table des matières 7

Chapitre 10

Produits supplémentaires1. JBoss RichFaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3452. Paramétrage et utilisation d'Eclipse . . . . . . . . . . . . . . . . . . . . . . . . 350

2.1 Paramétrage d'Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3512.2 Déploiement d'un projet à partir d'Eclipse . . . . . . . . . . . . . . . 3572.3 Import de projet existant dans votre espace de travail. . . . . . 3612.4 Créer un nouveau projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363

2.4.1 Projet EJB 2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3632.4.2 Projet EJB 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3702.4.3 Projet Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3712.4.4 Projet d'entreprise . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3752.4.5 Création d'une archive. . . . . . . . . . . . . . . . . . . . . . . . . 379

2.5 Résoudre quelques problèmes courants . . . . . . . . . . . . . . . . . 3802.5.1 Erreurs suite à un import . . . . . . . . . . . . . . . . . . . . . . 3802.5.2 Certaines vues ne sont pas disponibles . . . . . . . . . . . . 382

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385

JBossDéploiement et sécurisation d'applications JEE

8

Introduction

Nous ferons dans certains chapitres, dont celui­ci, un certain nombre de rappels sur les technologies J2EE et JEE pour :

bien préciser quels impacts ont les spécifications de ces technologies sur le serveur JBoss ;

présenter les choix possibles de codage et les déploiements qui en découlent ;

comprendre quels types d’erreurs peuvent arriver lors d’un déploiement et comment les corriger.

Mais le sujet de ce livre n’est pas le codage des EJB, servlet et JSP. Il en présente les principes, et est illustré de nombreux exemples, mais ne va pas aider le lecteur dans ces choix d’implémentation.

Par contre, cet ouvrage présente les services les plus importants de JBoss, leur fonctionnement, leur configuration, pour que le lecteur puisse en tirer le meilleur parti. JBoss étant un serveur d’application obéissant aux spécifications JEE, il faut évidemment comprendre les cycles de vie des objets qui seront pris en charge par les différents conteneurs.

Nous avons été souvent confrontés à des développeurs qui ont des connaissances de base dans le langage Java et dans les technologies J2EE/JEE, mais qui étaient décontenancés devant les problèmes de déploiement, de maintenance et de reprise de projets sous les serveurs d’application dont JBoss. Nous espérons que cet ouvrage satisfera leur curiosité et les poussera à aller plus loin.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz+YYgZKGRzIgLAA==-enidentnumber

Architecture des applications distribuées

Les applications informatiques ont pris une place centrale dans la plupart des entreprises. Les premières applications étaient monolithiques, d’une seule pièce. Ce type d’architecture se prête mal à la demande des entreprises. L’entreprise va demander à l’application informatique de lui fournir de nouveaux services en fonction de l’évolution de son environnement. Il est complexe d’ajouter de nouvelles fonctionnalités à une application monolithique, sans provoquer une régression de certaines parties du code. De plus, Internet a révolutionné le besoin de connectivité vis­à­vis de l’entreprise. En quelques années, les équipes de conception logicielle ont dû revoir leur mode de travail : connectivité, modélisation objet, utilisation de frameworks, etc. L’architecture des applications d’entreprise a largement évolué car le mode d’utilisation de ses applications a changé.

De nombreux postes informatiques sont maintenant reliés en réseau, ce qui permet à plusieurs salariés de travailler sur la même application. Des succursales de l’entreprise peuvent se connecter sur l’application, via Internet, pour gérer des données techniques, suivre les statistiques de ventes, effectuer des saisies, etc. Les clients de l’entreprise peuvent avoir accès, via son site web, à des données les concernant : factures, vérification de leurs cordonnées, changement de compte bancaire… Ils peuvent aussi être prévenus par SMS ou mail du suivi de leur commande, état de leur compte…

Les concepteurs d’applications doivent faire face à plusieurs défis :

des types variés de moyens de saisie et d’affichage de l’information : clients lourds, navigateurs, téléphone mobile…

des fonctionnalités qui évoluent : de nouveaux traitements doivent être ajoutés, des modifications de règles législatives, de nouveaux services client…

des règles de sécurité différentes pour les salariés, les succursales, les clients finaux, les partenaires…

Internet qui a considérablement modifié l’architecture avec l’utilisation de serveurs web, un nombre de connexions difficile à prévoir, la gestion des montées en charges, les connexions aux bases de données…

La conception d’une application amène à réfléchir sur :

ce qui est du domaine du métier de l’entreprise : gestion des clients, de la fabrication, des stocks…

ce qui est du domaine du support de l’application : base de données, gestion de la sécurité, transactions, réseau… Il faut remarquer que ces derniers points sont communs à toutes les applications.

Une architecture en couche permet de mieux maîtriser l’évolution de l’application, que ce soit au niveau fonctionnel, ou au niveau logique.

Le développeur n’a pas à se soucier du codage des contextes de transactions ou de ceux de l’authentification et sécurisation. Toutes ces fonctionnalités, présentes quelle que soit l’application et transversales aux couches applicatives, sont mises à disposition par le serveur. Il peut se consacrer au développement métier en utilisant les services précédemment décrits.

Sun, via la plate­forme JEE (Java Enterprise Edition), a mis en place une standardisation de ses services et la manière de les utiliser. Le développeur pourra se consacrer au codage des couches de présentation et métier, grâce à des

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

composants pris en charge par le serveur d’application.

Ces composants sont principalement :

Les servlets, qui décodent les en­têtes des requêtes HTTP et codent les en­têtes de réponses HTTP, gèrent les sessions HTTP, mettent à disposition du développeur des objets prenant en charge le cycle de vie de l’application Web. Les servlets sont complétées par d’autres composants et technologies : les JSP (Java Server Page), les balises personnalisées, l’EL (Expression Language), les JSF (Java Server Face)...

Ces composants sont pris en charge par le serveur Web.

Les EJB (Enterprise Java Bean) qui sont des composants dont le cycle de vie est géré par le conteneur d’EJBs. Le conteneur va, par ces composants, gérer les objets et services métier, la persistance, l’envoi et la réception de messages, les transactions... Le développeur doit se préoccuper uniquement de la logique métier :

comment calculer la facture

gérer un panier d’achat

Le conteneur prend en charge les aspects comme la sécurité ou les transactions.

l’utilisateur a­t­il le droit de calculer la facture ?

le paiment n’est pas validé sur le panier d’achat si un problème réseau survient.

Il existe deux grandes spécifications pour les EJB : celle des EJB 2 liée à J2EE, et celle des EJB 3 liée à JEE.

La connaissance de ces deux spécifications est actuellement nécessaire car un certain nombre de projets ayant vu le jour avant les EJB 3, il faut donc continuer à les faire vivre, les déployer et les administrer.

Les technologies incluses dans JEE permettent, entre autres :

la communication entre objets ;

la gestion des objets de réponses aux requêtes HTTP ;

la gestion des transactions ;

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

la persistance des objets avec les EJB entité ;

la communication asynchrone par message avec les EJB orienté message ;

la réalisation d’interface graphique ;

la gestion de la sécurité ;

la gestion de la répartition de charge.

Un serveur d’application JEE peut être vu comme la réunion :

d’un conteneur Web qui gère le cycle de vie des servlets et JSP ;

d’un conteneur EJB qui gère le cycle de vie des objets métier ;

d’un ensemble de services : accès aux bases de données, gestion des transactions…

1. L’invocation de méthodes distantes : RMI

Les objets des différentes couches sont susceptibles d’être invoqués par des objets situés sur une autre machine virtuelle. L’objet dont les méthodes peuvent être invoquées, est dit "objet serveur", l’objet invoquant les méthodes de l’objet serveur est dit "objet client". Il est évident que dans la réalité, les objets sont couramment serveur et client.

L’invocation directe d’une méthode dans une même machine virtuelle est simple : il s’agit d’un appel en mémoire vers une adresse, les arguments de la méthode appelée et sa valeur de retour sont passés en tant que références. C’est simple, efficace et rapide.

MonCalendrier cal = new MonCalendrier(); Integer annee = new Integer(2008); Date paques = cal.getDatePaques(annee);

L’invocation d’une méthode d’un objet situé dans une machine virtuelle différente est plus complexe. Nous n’avons comme lien entre les deux machines virtuelles que le réseau, sous protocole TCP/IP.

Il faut donc :

connaître l’IP de la machine où se trouve l’objet serveur ;

que l’objet serveur soit à l’écoute des demandes sur un socket de type serveur ;

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

que l’objet client initie une demande vers l’objet serveur via un socket ;

que les échanges soient effectués sur un protocole commun.

Donc, nous sommes loin de l’utilisation de l’opérateur point (.) pour invoquer la méthode distante. Le développeur passera sûrement plus de temps à élaborer un protocole de communication entre objets, qu’à se consacrer à l’écriture de son application !

Heureusement, nous pouvons utiliser la technologie RMI (Remote Method Invocation) qui va masquer, pour nous, l’ensemble des opérations décrites ci­dessus.

Une interface expose les méthodes distantes. Elle est implémentée par l’objet serveur et est utilisée par l’objet client. RMI est construit sur trois couches.

La première couche comprenant les stubs et skeletons est une couche proxy, ce qui permet au développeur de ne pas connaître les détails d’implémentation de RMI et d’utiliser l’opérateur point (.) pour l’invocation de la méthode distante. La classe stub, côté client, est créée par le compilateur rmic, présent dans le répertoire bin du JDK. Cette classe va sérialiser les paramètres passés à la méthode distante, et désérialiser la valeur de retour de la méthode distante. La classe skeleton, côté serveur, est chargée de désérialiser les paramètres reçus pour les transmettre à la méthode appelée, et de sérialiser la valeur de retour. Il faut donc que les classes des paramètres et de la valeur de retour soient sérialisables. Le passage des paramètres se fait alors par copie et non par référence.

La seconde couche, RRL (Remote Reference Layer), est chargée de la localisation de l’objet distant. Elle permet d’obtenir une référence à l’objet distant, à partir de la référence locale (le stub). Ce service est lié à un service de nommage, qui peut être un service JNDI (Java Naming and Directory Interface) ou un service de base appelé RMI registry, lancé avec la commande rmiregisty, situé dans le répertoire bin du JDK.

La troisième couche de transport est basée sur TCP/IP. Cette couche utilise les classes Socket et SocketServer.

La compréhension de RMI est primordiale pour aborder les applications distribuées en Java car RMI est utilisé pour la communication avec les EJB. Il faut noter qu’il existe des différences notables entre l’implémentation de RMI dans le JDK 1.1 et dans JDK 1.5. Les lecteurs intéressés pourront trouver plus de renseignements sur le site de Sun (http://java.sun.com).

Le processus de développement d’une application RMI est le suivant :

définir l’interface exposant les méthodes distantes ;

implémenter les méthodes distantes ;

développer le client ;

compiler les classes ;

générer les classes stub avec rmic ;

lancerrmiregistry ;

lancer l’application serveur ;

lancer l’application cliente.

L’ensemble des sources peut être téléchargé sur le site ENI. L’archive téléchargée est composée de plusieurs projets Eclipse qui vous permettront de tester les configurations proposées. Ces projets illustrent les différents points abordés dans cet ouvrage, et vous permettront de tester au fur et à mesure de la lecture les concepts abordés. Le projet comporte trois classes :

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

ITime qui est l’interface exposant la méthode qui peut être appelée par RMI : getDate() ;

TimeServeur qui est l’implémentation de ITime, et qui possède une méthode main() permettant de lancer le serveur ;

TimeClient qui invoque la méthode distante.

L’ensemble de ces classes est volontairement simple et sans package, afin de faciliter l’utilisation en ligne de commande.

L’interface ITime se présente ainsi :

import java.rmi.*;

import java.util.*;

public interface ITime extends Remote

public Date getDate() throws RemoteException;

La méthode distante est susceptible de déclencher une RemoteException. L’interface, qui expose nos méthodes métier, doit étendre l’interface java.rmi.Remote.

La classe d’implémentation TimeServeur se présente ainsi :

import java.rmi.*; import java.rmi.server.*; import java.util.*; public class TimeServeur extends UnicastRemoteObject implements Itime public TimeServeur() throws RemoteException super(); public Date getDate() log(); return new Date(); public static void main(String[] args) System.out.println("SERVEUR DEMARRE"); try ITime time = new TimeServeur(); Naming.rebind("TimeServeur", time); catch (Exception e) System.out.println("ERREUR : " + e); private void log() try System.out.println("Connexion du client : " + getClientHost()); catch (Exception e) System.out.println("ERREUR : "+e);

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

La classe d’implémentation doit étendre la classe java.rmi.server.UnicastRemoteObject qui permet l’export du stub, qui communiquera avec l’objet distant. La méthode main(...) de la classe permet l’inscription de l’instance de TimeServeur auprès du service de nommage, qui aura été lancé via rmiregistry. Cette inscription s’effectue par l’instruction Naming.rebind("TimeServeur", time), qui va lier l’instance time à la chaîne de caractère "TimeServeur". Ainsi, le client pourra récupérer, auprès du service de nommage, une instance de type ITime par la clé "TimeServeur". L’implémentation de la méthode getDate() ne fait que renvoyer la date système et appeler une méthode log(), qui affichera sur la console du serveur, les informations de connexion du client.

La classe cliente TimeClient se présente ainsi :

import java.rmi.*; import java.util.*; public class TimeClient public static void main ( String[] args ) try ITime service = (Itime) Naming.lookup("rmi://localhost/TimeServeur"); Date serverdate = service.getDate(); System.out.println("ServiceTime : " + serverdate); catch (Exception e) System.out.println("\nErreur\n" + e);

Cette classe recherche, auprès du service de nommage, un stub du type ITime.

Les méthodes main(...) des classes TimeServeur et TimeClient seront lancées dans des JVM différentes, donc, des consoles différentes.

Le lancement d’une console en ligne de commande s’effectue, sous Windows, par le menu démarrer ­ Exécuter, puis en entrant cmd. Ce lancement peut aussi s’effectuer par démarrer ­ Accessoires...

Le lancement d’une console, sous Ubuntu, s’effectue par Application ­ Accessoires ­ Terminal.

Ouvrez une première console pour la compilation et la génération du stub. Les classes sont compilées avec javac, puis le stub de la classe TimeServeur est généré avec rmic. L’exécution de ses deux lignes est effectuée dans le même répertoire que les classes.

Vérifiez que votre variable PATH contienne le répertoire bin du JDK installé sur votre machine.

javac *.java rmic TimeServeur

Un fichier TimeServeur_Stub.class a été créé par rmic. Vous trouverez ce fichier avec les autres classes générées par javac.

Avant de lancer le serveur, il faut lancer le service de nommage. Pour cela, ouvrez une nouvelle console puis exécutez la commande rmiregistry.

start remiregistry

Dans une nouvelle console, vous pouvez maintenant lancer le serveur.

java TimeServeur

Le lancement du serveur permet d’enregistrer la classe TimeServeur auprès du service de nommage, grâce à la méthode rebind(…) de la classe Naming. Il faut noter qu’il existe aussi, une méthode bind(…) qui permet l’enregistrement d’un objet ; la méthode rebind(…) permettant, en plus, de remplacer l’objet déjà existant.

public static void main(String[] args)

System.out.println("SERVEUR DEMARRE");

try

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

ITime time = new TimeServeur();

Naming.rebind("TimeServeur", time);

catch (Exception e)

System.out.println("ERREUR : " + e);

Dans une autre console, vous pouvez maintenant lancer l’application cliente.

java TimeClient

Le client peut maintenant récupérer une référence vers l’objet distant par un Naming.lookup() et l’utiliser.

public static void main ( String[] args )

try

ITime service = (Itime) Naming.lookup("rmi://localhost/TimeServeur");

Date serverdate = service.getDate(); System.out.println("ServiceTime : " + serverdate);

catch (Exception e)

System.out.println("\nErreur\n" + e);

La méthode lookup() attend une URL du type :

rmi://<hote>[<:port>]/<nom_du_service>

où <hote> identifie une machine sur le réseau par son nom ou son adresse IP, par défaut, le port est en 1099. Ici, localhost est utilisé car les tests sont effectués sur une même machine.

Le schéma suivant résume les appels effectués.

Il faut toujours avoir présent à l’esprit qu’un appel d’une méthode par RMI est plus consommateur de ressources et moins optimisé qu’un appel direct de la méthode au sein de la même machine virtuelle. Par RMI, les valeurs des paramètres envoyés à la méthode sont passées par copie, via la sérialisation. Il en est de même pour les valeurs de retour des méthodes. Lors d’un appel direct, ces mêmes valeurs auraient été passées par valeur. De plus, un appel au sein de la mémoire de la machine virtuelle sera toujours plus rapide qu’un appel via le réseau. Ce constat n’est pas sans conséquence pour la technologie des EJB 2, comme nous pourrons le voir par la suite.

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzE0DYdKGRzIgLAA==-enidentnumber

Rappels sur XML

Nous allons utiliser XML dans tous les descripteurs de déploiement, aussi nous allons voir brièvement les caractéristiques de cette spécification. Pour plus de précisions, reportez­vous aux spécifications XML gérées par le consortium W3C.

XML (eXtensible Markup Language) est un langage à base de balises. Les balises XML décrivent des contenus. L’objectif est de structurer le document, ce que ne fait pas HTML, qui décrit comment présenter le document dans un navigateur.

Un élément XML est structuré de la manière suivante :

<nom age="25"> => balise ouvrante Toto => corps </nom> => balise fermante

une balise ouvrante : ici, la balise nom. Le nom de la balise est libre, il peut être fixé par un schéma. La balise ouvrante est encadrée des signes inférieur (<) et supérieur (>).

des attributs et leur valeur : ici, nous n’avons qu’un attribut age. Le signe égal sépare l’attribut et sa valeur, la valeur d’un attribut doit être entre guillemets (") ou apostrophe (’). Les attributs ne sont pas obligatoires. Ils n’apparaissent que dans la balise d’ouverture.

le corps de l’élément : ici, Toto. Le corps de l’élément peut être vide, contenir un élément fils, du texte ou un élément fils et du texte.

la balise fermante, qui reprend le nom de la balise ouvrante précédée par le signe barre oblique (/). Elle ne comporte pas d’attributs. Elle est encadrée des signes inférieur (<) et supérieur (>).

les noms de balises ne peuvent pas comporter d’espace et commencer par un chiffre.

Toute balise ouvrante doit être fermée. Un élément qui ne comporte pas de corps peut être fermé directement.

<br></br> est équivalent à <br />

Tout document XML doit comporter un élément racine. Un document XML vide est un document dont l’élément racine est vide, et non pas un fichier vide.

<server> </server>

XML est sensible à la casse, les différences minuscule/majuscule sont prises en compte.

L’exemple suivant est incorrect :

<nom> </NOM>

Pour éviter l’analyse de certaines parties du document, le texte libre est mis dans des sections CDATA qui débutent par <![CDATA[ et finissent par ]]>.

<description> <![CDATA[Chap 2 - Calculette EJB2 generated by eclipse wtp xdoclet extension.]]> </description>

Les éléments ne peuvent pas se chevaucher. L’exemple suivant est incorrect :

<balise1> <balise2> </balise1> </balise2>

Les commentaires XML commencent par <!-- et finissent par -->.

<!-- commentaire valide -->

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz9ZamgaGRzIgLAA==-enidentnumber

Les éléments peuvent être mis en commentaire. Dans l’exemple suivant, l’élément <session> n’est plus pris en compte.

<!-- <session> <ejb-name>Calculette</ejb-name> <jndi-name>ejb2/calculette/remote</jndi-name> <local-jndi-name>ejb2/calculette/local</local-jndi-name> <method-attributes> </method-attributes> </session> -->

Attention : les commentaires ne peuvent pas être imbriqués, ce qui peut arriver lorsque vous mettez en commentaire des parties de fichier de configuration.

<!--

<session> <ejb-name>Calculette</ejb-name> <jndi-name>ejb2/calculette/remote</jndi-name> <local-jndi-name>ejb2/calculette/local</local-jndi-name> <method-attributes> </method-attributes> </session> <!--

write a merge file jboss-webservices.ent for webservice-description

-->

</enterprise-beans>

-->

Si l’ensemble de ces règles est observé, le document XML est dit "bien formé" (well formed).

Les documents XML sont analysés par des outils appelés parseurs (parsers).

Les spécifications Sun et JBoss imposent un vocabulaire pour les noms de balises et d’attributs. Ces mêmes spécifications imposent aussi l’ordre d’apparition des balises, leur nombre, le caractère obligatoire ou non d’un attribut...

Ces spécifications sont appelées schéma. Il en existe deux types :

les DTD (Document Type Description), les plus simples ;

les XMLSchemas, plus complexes mais beaucoup plus riches.

Vous retrouverez les schémas en en­tête des fichiers XML.

Mise en place d’une DTD :

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN"

"http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">

<jboss> <enterprise-beans> ... </jboss>

Mise en place d’un XMLSchema :

<?xml version="1.0" encoding="UTF-8"?> <ejb-jar id="ejb-jar_1" xmlns="http://java.sun.com/xml/ns/j2ee"

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz9ZamgaGRzIgLAA==-enidentnumber

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1">

... </ejb-jar>

Les parseurs, à l’aide des schémas, vont pouvoir analyser si votre fichier de configuration obéit bien aux spécifications. Un document bien formé et conforme à son schéma est dit valide.

Il est important de vérifier dans les logs si le non fonctionnement d’un EJB, ou d’une application Web, n’est pas dû à un fichier de configuration non valide. Le parseur de JBoss vérifie les fichiers XML avant de monter le module dans son conteneur.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz9ZamgaGRzIgLAA==-enidentnumber

Rappels sur J2EE 1.4

Avec la spécification J2EE 1.4, un EJB est un composant constitué, au minimum de (ou des) :

une (ou deux) interface(s) de création ;

une (ou deux) interface(s) d’exposition des méthodes ;

classes qui implémentent les interfaces ;

un fichier de déploiement ejb­jar.xml ;

un (ou plusieurs) fichier(s) de configuration ­ propre(s) au serveur d’application.

L’objet qui est codé par le développeur contient l’implémentation des méthodes métier. Cette classe doit implémenter une interface du typejavax.ejb.Sessionbean ou javax.ejb.EntityBean. La spécification EJB 2 demande ensuite deux types d’interfaces pour l’exposition :

des méthodes de création ;

des méthodes métier.

N’oubliez pas que le cycle de vie de l’EJB est géré par le conteneur d’EJB. Le client de l’EJB ne l’instancie pas directement, il passe par les méthodes de création (create(), findByPrimaryKey(), etc) qui sont exposées via une interface, étendant l’interfacejavax.ejb.EJBHome.

De même, les appels vers les méthodes métier implémentées dans l’EJB passeront par l’interface, exposant les méthodes métier. Le développeur doit donc, déclarer les méthodes métier accessibles en les exposant dans une interface, étendant l’interface javax.ejb.EJBObject.

Les interfaces, qui étendent javax.ejb.EJBHome etjavax.ejb.EJBObject, sont dites des interfaces distantes car les appels sur les méthodes exposées par ces interfaces sont effectués via RMI. Or, de nombreux appels sont effectués entre objets qui sont déployés sur le même serveur, donc dans la même machine virtuelle. Par exemple, une servlet d’une application Web qui utilise un EJB de session, ou un EJB de session qui utilise un EJB entité. Donc dans ce cas, l’invocation des méthodes via RMI est très couteuse, car complètement inutile. Pour y remédier, la spécification EJB 2 ajoute deux interfaces qui permettent de déclarer les méthodes de création et les méthodes métier, qui seront accessibles localement, au sein de la même machine virtuelle :

l’interface javax.ejb.EJBLocalHome pour les méthodes du cycle de vie ;

l’interface javax.ejb.EJBLocalObject pour les méthodes métier.

Le développeur doit donc :

créer une classe d’implémentation pour implémenter une interface javax.ejb.SessionBean ou javax.ejb.EntityBean.

exposer les méthodes du cycle de vie dans des interfaces qui permettront un accès :

distant via RMI (remote) en étendant l’interface javax.ejb.Home ;

au sein d’une même machine virtuelle (local) en étendant l’interface javx.ejb.LocalHome.

exposer les méthodes métier dans des interfaces qui permettront un accès :

distant en étendant l’interface javax.ejb.EJBObject ;

local en étendant une interface javax.ejb.EJBLocalObject.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzn6WykaGRzIgLAA==-enidentnumber

écrire les descripteurs de déploiement de l’EJB. Ce fichier au format XML décrit les propriétés des EJB, qui constitueront l’archive. Ce fichier s’appelle le fichier ejb­jar.xml.

écrire un fichier de déploiement spécifique au serveur, le fichier jboss.xml pour JBoss, qui décrit, entre autres, les noms JNDI des EJB.

Nous pouvons voir que l’écriture d’un EJB peut paraître assez complexe, bien que le véritable travail ne concerne qu’une seule classe : la classe d’implémentation. Heureusement, il existe des outils pour automatiser la création du composant :

soit en utilisant des scripts Ant

soit en utilisant les XDoclets, comme c’est le cas sous Éclipse Europa.

Vous trouverez en annexe les paramétrages à effectuer pour l’utilisation de XDoclet. Le développeur crée uniquement sa classe d’implémentation, ensuite, l’outil créera les interfaces et les fichiers XML nécessaires.

Les objets clients n’interagissent jamais avec la classe implémentant les interfaces. C’est le serveur qui gère, par l’intermédiaire de classes d’interpositions, le cycle de vie et les invocations des méthodes de l’EJB.

Il est important de noter que les accès aux EJB peuvent se faire de deux façons :

par RMI, si le client de l’EJB est un objet déployé sur une machine virtuelle différente de celle de l’EJB ;

sans RMI, si le client de l’EJB est dans la même machine virtuelle.

Il faut donc, très tôt, se demander comment sera déployée l’application. Si l’application Web est déployée sur le même serveur que la couche métier, il faut privilégier les appels sans RMI. Un appel par RMI est toujours plus coûteux en terme de performance (passage des paramètres par copie, sérialisation) qu’un appel direct en mémoire.

Il faut noter que sous JBoss, si le client et l’EJB sont dans la même machine virtuelle, même si le client appelle les méthodes via les interfaces distantes (donc par RMI), JBoss invoquera les méthodes via les interfaces locales, si elles existent.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzn6WykaGRzIgLAA==-enidentnumber

Rappels sur JEE 5

Le codage des EJB a été largement simplifié avec la spécification EJB 3. Les mécanismes de base sont les mêmes : RMI, JNDI, pas d’invocation directe des méthodes des EJB, cycle de vie de l’EJB géré par le conteneur.

Les EJB 3 sont basés sur de simples classes POJO (Plain Old Java Object, objet de base type JavaBean). L’interface Home de création a disparu et le fichier XML ejb-jar.xml décrivant le composant n’est plus obligatoire. Cette simplification est liée à l’évolution du langage (apparition des annotations avec Java 5) et aux bénéfices issus des travaux sur les frameworks Spring, Hibernate...

Les différents services d’une application doivent pouvoir se localiser. Cette opération peut prendre de multiples formes :

création d’un objet et utilisation directe de celui­ci ;

recherche du service en utilisant JNDI ;

emploi de fabrique pour créer le service ;

implémentation des services comme singleton.

Le code client du service peut, alors, devenir très vite significativement dépendant de l’implémentation, ou vite peu lisible par le codage des recherches. Cette localisation est effectuée classiquement par l’intermédiaire de JNDI sur les serveurs d’application.

Les dépendances peuvent être éliminées en confiant à un tiers le soin de mettre en relation les objets, c’est l’inversion de contrôle. Les composants sont, alors, reliés automatiquement avant leur utilisation.

Pour bien comprendre comment les conteneurs JEE 5 gèrent le cycle de vie des composants, il est primordial de comprendre les implémentations possibles du pattern Inversion of Control (IoC), appelé aussi Dependency Injection.

L’inversion de contrôle (IoC ­ Inversion of Control) n’est en rien, une évolution du langage. C’est un modèle de conception qui propose de séparer les problématiques techniques (aspects) des problématiques métier dans une application. C’est une application tiers, le framework, qui mettra en liaison les objets.

1. Les annotations

Le système des annotations est une des évolutions importantes apportées par Java 5, puis enrichies par Java 6. C’est une véritable alternative aux fichiers de configuration XML et aux outils tiers tels que XDoclet. La maintenance est largement simplifiée car la prise en compte des changements dans le code source est, directement, sous la responsabilité des outils du langage, ou des conteneurs, sans avoir à synchroniser un fichier XML ou à réeffectuer une compilation avec XDoclet.

Les annotations sont des métadonnées ajoutées au code. Le développeur ajoute les annotations au code source et elles peuvent se propager jusqu’à l’exécution.

Il existe, dans la spécification des annotations, des "super annotations" : les méta­annotations, qui permettent d’annoter les annotations. Ces méta­annotations sont situées dans le package java.lang.annotation.

L’ensemble des annotations (et méta­annotations) utilisables avec le JDK 6 héritent de l’interface java.lang.annotation.Annotation. Les conteneurs, dont JBoss, ajoutent leur propre jeu d’annotations.

Méta­annotation Objectif

@Documented Indique à l’outil javadoc qu’il doit prendre en compte cette annotation.

@Inherited Permet l’héritage entre annotations.

@Retention Détermine la politique de propagation de l’annotation.

@Target Détermine la cible de l’annotation.

@SupressWarning Supprime les avertissements sur un élément.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzW2LYM6KRzIgLAA==-enidentnumber

La méta­annotation @Retention permet de fixer le mode de propagation de l’annotation.

RetentionPolicy.SOURCE : les annotations ne seront pas enregistrées dans le fichier *.class. Elles sont utilisables par les outils manipulant les fichiers sources (compilateur, javadoc…). Exemple : l’annotation standard @Overrides.

RetentionPolicy.RUNTIME : les annotations sont enregistrées dans le fichier *.class et sont accessibles par la machine virtuelle, lors de l’exécution. Ces annotations sont utilisées via l’API de réflexion. Les annotations JBoss (comme @LocalBinding) sont du type RetentionPolicy.RUNTIME.

RetentionPolicy.CLASS : les annotations sont enregistrées dans le fichier *.class. Elles ne sont pas utilisées par la machine virtuelle, mais peuvent l’être par les outils manipulant les fichiers *.class.

L’injection de dépendance utilise les annotations et la réflexion. Un exemple simple permet de mieux comprendre comment les conteneurs peuvent injecter dans les membres de nos classes les ressources voulues (EJB, source de données…).

L’ensemble des fichiers sources qui suivent sont téléchargeables sur le site ENI, sous le projet Annotations.

Il nous faut d’abord créer notre propre annotation :

package fr.eni.editions.jboss.annotations;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD)

public @interface Message

String value();

Le type de notre annotation Message est @interface. Il possède une méthode value() qui représente un attribut de notre annotation.

Nous pouvons, maintenant, utiliser notre annotation dans la classe TestMessage :

package fr.eni.editions.jboss.annotations;

public class TestMessage

@Message("Hello world") private String monMessage;

public void afficher()

System.out.println("Valeur du message : "+monMessage);

Il n’y a qu’une seule méthode dans notre annotation : value(). Nous pouvons donc éviter de préciser le nom de l’attribut de Message. Si cela n’avait pas été le cas, nous aurions dû avoir :

@Message(value="Hello world") private String monMessage;

L’objectif de l’utilisation de notre annotation est donc, d’injecter la valeur de l’attribut value de Message dans la propriété monMessage de TestMessage. Cette injection est effectuée par une application tiers, en général un framework. Dans notre exemple il s’agira d’une simple classe.

Nous devons donc maintenant, coder l’injection :

package fr.eni.editions.jboss.annotations;

import java.util.*;

import java.lang.reflect.*;

public class MessageProcess

// code permettant l’injection de dépendance public static void injecter(Object obj) throws Exception

for(Field field : obj.getClass().getDeclaredFields())

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzW2LYM6KRzIgLAA==-enidentnumber

if(field.isAnnotationPresent(Message.class))

Message annotation = (Message)field.getAnnotation (Message.class);

field.setAccessible(true);

field.set(obj,annotation.value());

La méthode injecter(Object obj) de la classe MessageProcess permet de réaliser cette injection. Pour chaque champ field de l’instance obj, il y a vérification de la présence de l’annotation. Si c’est le cas, la valeur de l’attribut value de l’annotation est récupérée pour mettre à jour le champ.

C’est l’application qui mettra en œuvre l’injection :

package fr.eni.editions.jboss.annotations;

public class Main

public static void main(String[] args) throws Exception

TestMessage test = new TestMessage();

MessageProcess.injecter(test);

test.afficher();

Cet exemple permet de mieux comprendre comment les conteneurs compatibles avec les spécifications JEE 5 peuvent automatiquement utiliser les annotations pour mettre des ressources dans un contexte JNDI, injecter des ressources du contexte JNDI vers les membres de nos classes.

2. Injection par proxy

La journalisation, les messages de debug, les autorisations, les transactions sont des problématiques récurrentes en programmation. Si nous prenons l’exemple d’une journalisation, il nous faut une classe qui permette la journalisation, appelée ici Logger, et une qui utilise la journalisation, Service. Les méthodes ayant besoin de journaliser appelleront les méthodes de la classe Logger. Cela sera identique pour toute classe utilisant la journalisation.

package fr.eni.editions.jboss.aop;

public class Logger

public void debutLog()

System.out.println("début de journalisation");

public void finLog()

System.out.println("fin de journalisation");

package fr.eni.editions.jboss.aop;

public class Service

Logger logger = new Logger();

public void executer()

logger.debutLog(); System.out.println(">> Méthode ’executer()’");

logger.finLog();

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzW2LYM6KRzIgLAA==-enidentnumber

Cette illustration peut être appliquée à l’authentification, les transactions, etc. Nous voyons ainsi notre code émaillé d’appels vers des méthodes de classes qui n’ont rien à voir avec notre code métier. Ces appels peuvent nuire à la lisibilité de nos méthodes métier. Une dépendance est créée entre des classes qui a priori n’ont rien à voir entre elles. Les services de journalisation, authentification... sont des services transversaux à notre application.

Pour supprimer les dépendances entre ces classes, un des moyens est de déléguer à un framework l’appel des méthodes. Nous illustrerons ceci par l’exemple de la mise en place d’un proxy, dont la méthode invoke() sera appelée avant la méthode executer() de notre classe Service.

L’ensemble des fichiers sources qui suivent sont téléchargeables sur le site ENI, sous le projet Exemple IoC.

La classe Service ne contient plus d’appels aux méthodes de la classe Logger :

package fr.eni.editions.jboss.aop;

public class Service implements Iservice

public void executer()

System.out.println(">> Méthode ’executer()’");

La classe Service n’est donc plus liée à la classe Logger, chargée de la journalisation. Pour que la classe Service utilise Logger, il nous faut un intermédiaire qui mettra en relation ces deux classes, en changeant le comportement de la méthode executer().

Les classes Main, ServiceFactory et LoggerProxyHandler jouent le rôle du framework en mettant en liaison, ou non, les classes Logger et Service, en fonction du paramètre passé à la méthode create() de ServiceFactory. La journalisation sera activée si le paramètre passé est true.

package fr.eni.editions.jboss.aop;

public class Main

public static void main(String[] args)

IService s = ServiceFactory.create(true);

s.executer();

La méthode create() de ServiceFactory retourne une instance d’un proxy sur Service si la demande de journalisation est activée par le paramètre booléen log. Si la demande de journalisation n’est pas activée, la méthode renvoie une instance de Service.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzW2LYM6KRzIgLAA==-enidentnumber

package fr.eni.editions.jboss.aop;

import java.lang.reflect.Proxy;

import java.lang.reflect.InvocationHandler;

public class ServiceFactory

public static IService create(boolean log)

IService service = new Service();

if (log)

InvocationHandler handler = new LoggerProxyHandler(service);

IService proxy = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),

new Class[]

IService.class , handler);

return proxy;

else

return service;

La classe LoggerProxyHandler doit mettre en relation une instance de classe quelconque avec la classe Logger. Le constructeur de LoggerProxyHandler reçoit l’instance de la classe sur laquelle la journalisation doit être effectuée.

La méthode invoke() de LoggerProxyHandler sera appelée à la place de la méthode executer() de la classe Service. Cette méthode mettra alors en place la journalisation avant d’invoquer la méthode executer() de la classe Service.

package fr.eni.editions.jboss.aop;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

public class LoggerProxyHandler implements InvocationHandler

Object subject; public LoggerProxyHandler(Object subject)

this.subject = subject;

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable

Logger ctx = new Logger();

ctx.debutLog(); Object returnValue = method.invoke(subject, args); ctx.finLog(); return returnValue;

Le diagramme de séquences suivant présente les échanges de messages entre les instances.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzW2LYM6KRzIgLAA==-enidentnumber

Dans le monde réel, les frameworks peuvent avoir des implémentations différentes, et souvent, la description des liaisons est effectuée par le biais d’un fichier XML ou des annotations.

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzW2LYM6KRzIgLAA==-enidentnumber

Présentation du serveur d’applications JBoss

JBoss est un serveur d’application écrit en Java. Il peut être employé sur tout système équipé d’une JVM (Java Virtual Machine).

JBoss a été développé au sein de la société JBoss Inc., créée par Marc FLEURY, le concepteur de la première version de JBoss.

JBoss a obtenu la certification en J2EE 1.4 en juillet 2004. Puis, Red Hat achète JBoss Inc. en avril 2006 et JBoss Enterprise devient une division de Red Hat

JBoss peut être obtenu sous licence LGPL auprès de Jboss.org, qui regroupe les projets JBoss et la communauté des développeurs JBoss. Dans ce cas, il n’y a pas d’autre support que celui offert par la communauté.

Il peut aussi être obtenu de manière commerciale auprès de JBoss Enterprise. Il est, alors, possible de bénéficier d’une ligne de produits et de différents services : support technique, programmes de formation, etc.

La communauté JBoss gère plusieurs projets ou sous­projets :

projets serveurs : le serveur JBoss, JBoss pour le web, micro­conteneur de JBoss...

projets d’intégration : messageries, web services, transaction...

outils : outils de développement, de profilage, de test ;

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzGhUJwWaSzIgLAA==-enidentnumber

interfaces web : bibliothèques pour Ajax, JSF ;

frameworks : Seams, EJB 3, AOP, Hibernate ;

portlets, applications qui peuvent être incluses dans un portail Web : forum, wiki, blog ;

sécurité...

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzGhUJwWaSzIgLAA==-enidentnumber

Téléchargement

Il est nécessaire de vous assurer que vous avez, au moins, la version 5 de la JRE de Sun d’installée pour tester les EJB 3. Si vous utilisez Eclipse pour développer et contrôler JBoss, il vous faudra un JDK, et non un JRE.

Pour connaître la version de la JVM installée, exécutez dans une console :

$ java -version

Nous verrons comment installer et configurer JBoss sous les systèmes d’exploitation Windows et Ubuntu (distribution Linux). Seules certaines manipulations liées au système d’exploitation sont différentes : gestion des arborescences de fichier, utilisation d’éditeurs de texte. Les contenus des répertoires de JBoss, les fichiers de configuration de JBoss sont identiques.

À titre indicatif, les machines qui ont été utilisées lors de l’écriture de ce document sont les suivantes :

un portable sous Windows XP, microprocesseur Intel T2400 à 1,83 GHz, 1 Go de RAM, JDK 6, Eclipse 3.2 ;

une station fixe sous Ubuntu, microprocesseur Intel Dual Core 6320 à 1,86 GHzGHz, 2 Go de RAM, JDK 6, Eclipse 3.2.

1. Téléchargement de JBoss

Le téléchargement du serveur JBoss s’effectue sur le site de sourceforge, via le site communautaire de JBoss labs.jboss.com (cette adresse est aussi accessible par www.jboss.org).

Lorsque vous accédez à la page principale de labs.jboss.com, sélectionnez le lien Resources.

Vous arrivez alors sur la page décrivant les téléchargements possibles, sélectionnez le lien JBoss Application Server.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz0w9DVaKRzIgLAA==-enidentnumber

Maintenant, vous devez choisir la version de JBoss à installer. Sur cette page, vous pourrez télécharger des versions antérieures qui peuvent s’avérer nécessaires si votre application JEE est en production depuis un certain temps. Les versions bêta vous permettront de vérifier le bon fonctionnement de vos applications en cas d’évolution du serveur et de profiter de ses nouveaux services. Les versions bêta sont évidemment à proscrire pour le serveur en production. Elles sont aussi à éviter lors du développement d’application car il n’est pas nécessaire de cumuler les difficultés liées au développement et aux instabilités de certains services du serveur. Nous choisirons ici la dernière version stable à l’heure où nous écrivons ces lignes, soit la version 4.2.2.GA. Sélectionnez maintenant le lien Download.

Vous arrivez maintenant sur la page de téléchargement de JBoss, sur sourceforge. Sélectionnez les liens jboss­4.2.2.GA.zip puis jboss­4.2.2.GA.zip.MD5. Le fichier MD5 vous permettra de vérifier que l’archive zip récupérée n’est pas corrompue.

À l’aide d’un utilitaire type md5sum, vous pouvez vérifier si la signature de votre archive zip correspond à la signature contenue dans la fichier MD5. Dans une console DOS, exécutez la commande qui génère la somme de contrôle.

md5 <chemin du fichier>jboss-4.2.2.GA.zip

La valeur de la signature générée et celle contenu dans le fichier MD5 doivent être identiques.

Le principe est le même que sous Windows, avec les commandes md5sum et cat.

Vérification MD5 sous Windows

Vérification MD5 sous Ubuntu

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz0w9DVaKRzIgLAA==-enidentnumber

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz0w9DVaKRzIgLAA==-enidentnumber

Installation de JBoss

Une fois la vérification de la signature MD5 effectuée, vous pouvez décompresser le fichier jboss­4.2.2.GA.zip vers un répertoire cible. Ici, la décompression a été effectuée dans le répertoire C:\SERVEURS pour Windows et /home/franck/SERVEURS sous Ubuntu. Sous les deux systèmes d’exploitation, il y a création d’un répertoire jboss­4.2.2.GA dans le répertoire cible, qui contient la même arborescence de répertoires.

Il existe une aide à l’installation pour JBoss : jems-installer. Nous ne l’avons pas utilisée ici, car la dernière version stable de JBoss n’y est pas incluse. Le fichier jems-installer-1.2.0.GA.jar contient la version 4.0.5.GA. Si vous souhaitez utiliser ce fichier jar, vous devez le lancer avec la commande :

java -jar jems-installer-1.2.0.GAR.jar

Un assistant graphique va alors vous aider à installer JBoss. Restez sur les choix par défaut de cet assistant, nous verrons plus tard, comment changer le type de serveur.

Utilisation de l’installateur JEMS Installer

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzQ5DaYaKRzIgLAA==-enidentnumber

Lors du choix du serveur par défaut, ne sélectionnez pas all, surtout dans le cas de l’installation sur plusieurs machines en réseau. En effet, un cluster de serveurs serait installé. Nous verrons plus tard comment gérer les fermes de serveur.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzQ5DaYaKRzIgLAA==-enidentnumber

Structure des répertoires

Le répertoire jboss­4.2.2.GA contient les répertoires suivants :

bin : ce répertoire contient différents utilitaires (comme twiddle), ainsi que les scripts de démarrage et d’arrêt du serveur, pour Windows (extension bat) et LINUX (extension sh).

client : ce répertoire contient les bibliothèques nécessaires à une application cliente pour se connecter au serveur JBoss. Il peut s’agir d’une application cliente type client lourd (IHM en swing, par exemple) ou un autre conteneur JEE.

docs : ce répertoire contient l’ensemble des DTD et XML Schema utilisés par les fichiers XML de configuration et de déploiement.

lib : ce répertoire contient l’ensemble des archives java utilisées par le serveur JBoss.

server : ce répertoire contient des sous­répertoires, correspondant à différents types de serveurs pouvant être démarrés. Par défaut, il contient trois configurations de serveurs : all, default, minimal. Chacun de ces répertoires contient les bibliothèques, fichiers de configuration, services nécessaires au bon fonctionnement d’un type de serveur.

1. Structure du répertoire de type server

Chaque sous­répertoire du répertoire server correspond à un type de serveur. Le nom du serveur correspond au nom du répertoire. Le serveur default est celui qui est lancé par défaut, lors du démarrage de JBoss. Un type de serveur se différencie d’un autre par les services qui sont exécutés par JBoss. Tous les serveurs ont une arborescence commune.

Les répertoires contenus dans un répertoire de type server sont les suivants :

conf : répertoire contenant les fichiers de configuration du serveur, sous forme de fichiers XML ou properties. Il contient notamment le fichier jboss­service.xml décrivant les services à activer.

data : répertoire utilisé par les services.

deploy : répertoire de déploiement à chaud des applications JEE (extensions ear, war ou jar) et des services (extension sar).

lib : répertoire contenant les bibliothèques qui seront accessibles dans le classpath du serveur. Des bibliothèques externes peuvent y être ajoutées.

log : répertoire où sont enregistrés les journaux des événements.

tmp : répertoire utilisé par JBoss lors des déploiements, et lors des sérialisations d’objets.

work : répertoire utilisé par Tomcat (conteneur web de JBoss) pour compiler les pages JSP.

Certains répertoires peuvent ne pas être présents juste après la décompression de l’archive. Ils seront créés par JBoss en fonction des besoins des applications déployées.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzq76ncqKRzIgLAA==-enidentnumber

Le répertoire deploy qui est le répertoire de déploiement des applications contient déjà un certain nombre de fichiers. Les fichiers présents peuvent dépendre de la configuration de votre serveur. Par exemple, le répertoire deploy de la configuration minimale est vide.

Voici quelques fichiers de ce répertoire deploy :

bsh­deployer.xml : fichier de configuration du service de déploiement des scripts shell comme service JBoss ;

client­deployer­service.xml : service de déploiement des applications clientes ;

ear­deployer.xml : service de déploiement des fichiers EAR (Enterprise ARchive) ;

ejb­deployer.xml : service de déploiement des modules EJB ;

hsqldb­ds.xml : fichier de configuration de la base de données embarquée dans JBoss : Hypersonic ;

http­invoker.sar : service de support du protocole RMI sur HTTP ;

jboss­aop­jdk50.deployer : service de configuration et déploiement des applications JBoss AOP ;

jboss­bean.deployer : permet le déploiement de classes POJO contenues dans des fichiers .beans ;

jmx­console.war : application web de gestion du serveur de MBean.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzq76ncqKRzIgLAA==-enidentnumber

Démarrer et arrêter le serveur

Avant de lancer JBoss, assurez­vous que la variable d’environnement JAVA_HOME est bien positionnée sur votre JDK. Si ce n’est pas le cas, vous pouvez la positionner en mode console, avant le lancement de JBoss. Faites attention à bien positionner la variable JAVA_HOME dans la même console qui lancera JBoss.

Sous Windows :

set JAVA_HOME=<répertoire d’installation du JDK>

Sous Ubuntu :

export JAVA_HOME=<répertoire d’installation du JDK>

D’autres variables d’environnements peuvent être, éventuellement, utilisées par JBoss.

JAVA_OPTS : options supplémentaires passées à la machine virtuelle, comme la taille du tas ­Xmx512M.

JBOSS_CLASSPATH : entrées de classpath supplémentaires.

MAX_FD : sous LINUX, nombre maximum de descripteurs de fichiers, utilisés par JBoss.

JAVA : nom du binaire java, java par défaut.

1. Sous Windows

Dans une console, positionnez­vous dans le répertoire bin du répertoire d’installation de JBoss, puis exécutez la commande run.

2. Sous Ubuntu

Dans une console, positionnez­vous dans le répertoire bin du répertoire d’installation de JBoss, puis exécutez la commande ./run.sh.

3. Suivi du démarrage

Quel que soit votre système d’exploitation, vous pouvez observer le démarrage de JBoss dans la console.

Vous pourrez y vérifier la prise en compte de la bonne machine virtuelle.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzCRbakqKRzIgLAA==-enidentnumber

Quand le temps de démarrage du serveur s’affiche, il est réellement actif.

Vous remarquerez que le service HTTP est en écoute sur le port 8080.

Par défaut, les services JBoss écoutent l’adresse IP 127.0.0.1, soit l’adresse locale. Vous pouvez le vérifier dans la console de démarrage.

Ce qui explique que sur votre réseau, vous pouvez visualiser la page d’accueil de JBoss sur la machine sur laquelle s’exécute JBoss, à l’URL http://localhost:8080, mais que vous ne pouvez pas voir cette même page sur une autre machine.

L’option de la ligne de commande-b ou --host permet de spécifier une autre adresse d’écoute.

run -b 192.168.2.174

ou

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzCRbakqKRzIgLAA==-enidentnumber

run --host=192.168.2.174

Si l’adresse IP 0.0.0.0 est précisée, les services JBoss écouteront sur toutes les adresses.

D’autres options peuvent être précisées sur la ligne de commande :

La forme simplifiée d’une option commence par un tiret, sa forme complète par deux tirets. Les options s’utilisent soit :

avec leur forme simplifiée : -b 192.168.2.174, et alors un espace sépare l’option et sa valeur ;

soit avec leur forme complète : --host=192.168.2.174, et alors un signe égal sépare l’option et sa valeur.

4. Test de JBoss

Pour tester le bon fonctionnement de cette installation, vous pouvez, dans un navigateur, taper l’url http://localhost:8080/. La page d’accueil de JBoss s’affiche : le serveur est démarré correctement.

­h ­­help page d’aide.

­V ­­version affichage de la version.

­D<name>[=<value>] ajout d’une propriété système.

­d ­­bootdir=<dir> répertoire de prise en compte des patches de démarrage.

­p ­­patchdir=<dir> répertoire de prise en compte des patches.

­n ­­netboot=<url> démarrage réseau.

­c ­­configuration=<name> nom de la configuration serveur à lancer, par défaut default.

­B ­­bootlib=<filename> ajout d’une librairie au classpath de démarrage du serveur.

­L ­­library=<filename> ajout d’une librairie au classpath.

­C ­­classpath=<url> ajout d’une URL au classpath.

­p ­­properties=<url> lecture de propriétés système à partir d’une URL.

­b ­­host=<host ou IP> adresse d’écoute pour les services JBoss, par défaut 127.0.0.1.

­g ­­partition=<name> nom du service HA (highly available), par défaut DefaultDomain.

­u ­­udp=<IP> adresse multicast UDP.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzCRbakqKRzIgLAA==-enidentnumber

5. Arrêt de JBoss

Sous Windows ou Ubuntu, vous pouvez arrêter le serveur avec la combinaison de touches [Ctrl] C dans la console où le serveur est démarré.

Depuis le répertoire bin de JBoss, vous pouvez aussi lancer le script d’arrêt du serveur : shutdown. Attention, pour que le script fonctionne, il faut que la variable d’environnement JAVA_HOME soit positionnée sur le JDK.

Sous Windows :

C:\SERVEURS\jboss-4.2.2.GA\bin> shutdown -S

Sous Ubuntu :

$ ./shutdown.sh -S

6. Arrêt et démarrage des autres configurations serveur

Lors du test de démarrage précédent, effectué avec la commande run, la configuration par défaut a été exécutée. Cette configuration par défaut correspond au répertoire <dossier installation JBoss>/server/default. Ce serveur par défaut correspond à un serveur aux spécifications JEE, sans le support du clustering.

Il existe deux autres configurations installées : minimal et all. La première correspond à un serveur minimum, qui lance uniquement JNDI (Java Naming and Directory Interface) comme service de base. Ce serveur ne correspond pas aux spécifications JEE.

La deuxième all est un serveur complet JEE, avec le support du clustering.

Le lancement d’un serveur autre que celui par défaut, s’effectue avec la commande run, en précisant la configuration à lancer.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzCRbakqKRzIgLAA==-enidentnumber

a. Pour lancer le serveur minimal

Sous Windows :

C:\SERVEURS\jboss-4.2.2.GA\bin> run -c minimal

ou

C:\SERVEURS\jboss-4.2.2.GA\bin> run --configuration=minimal

Sous Ubuntu :

$ ./run -c minimal

ou

$ ./run --configuration=minimal

Le temps de démarrage du serveur est très bref. Le dernier message de la console indique ce temps.

Attention : dans cette configuration minimum, vous ne pouvez pas vous connecter sur l’url http://localhost:8080/. car le serveur web n’est pas lancé. De même, vous ne pourrez pas arrêter le serveur avec la commande shutdown, car le port d’écoute d’arrêt du serveur n’est pas activé. Pour arrêter ce serveur, il faut effectuer un [Ctrl] C dans la console où s’exécute le serveur.

b. Pour lancer la configuration all

Sous Windows :

C:\SERVEURS\jboss-4.2.2.GA\bin> run -c all

ou

C:\SERVEURS\jboss-4.2.2.GA\bin> run --configuration=all

Sous Ubuntu :

$ ./run -c all

ou

$ ./run --configuration=all

Remarquez le temps de lancement de ce serveur.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzCRbakqKRzIgLAA==-enidentnumber

Ce serveur peut être testé avec l’url http://localhost:8080/ et arrêté avec la commande shutdown.

Les manipulations et commandes étant les mêmes, quel que soit le système d’exploitation, les exemples ne seront plus répétés pour Windows et Ubuntu (ou tout autre système LINUX).

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzCRbakqKRzIgLAA==-enidentnumber

Déploiement et repli des applications JEE

Dans cet ouvrage, nous étudierons les applications J2EE et JEE 5, qui correspondent aux EJB 2 et EJB 3. Les nouveaux projets démarrent avec les EJB 3, mais il est important de comprendre comment sont déployées les anciennes applications EJB 2, car il faut en assurer la maintenance et l’évolution.

Vous trouverez en téléchargement sur le site de ENI les exemples cités dans cet ouvrage. L’accent sera particulièrement mis sur les descripteurs de déploiement, et sur le code Java qui est pertinent par rapport aux options de déploiement.

Comme rappelé dans le chapitre Introduction, les applications déployées sur le même serveur sont dans la même machine virtuelle. Il faut donc utiliser les interfaces locales. L’appel des interfaces distances (remote) met en jeu tout le processus RMI, avec la sérialisation des arguments et de valeurs de retour, donc un passage par copie. Lors de l’appel par les interfaces locales, seules les références des arguments et valeur de retour sont passées.

Le déploiement des applications est habituellement fait sous forme d’un fichier ear qui regroupe les applications web, les composants ejb et les bibliothèques java communes. Mais lors d’un développement ou lors des tests, il est courant de déployer module par module, sans passer par une archive ear. Nous allons donc présenter ces différents types de déploiement. Nous reviendrons sur ce sujet lorsque nous aborderons le chapitre sur JNDI.

Dans tous les cas le déploiement s’effectue dans le répertoire deploy de la configuration serveur, soit :

<dossier installation jboss>/server/default/deploy

si JBoss a été lancé avec le serveur par défaut.

Les déploiements peuvent être effectués par copie des archives dans le répertoire deploy, ou par l’intermédiaire de l’outil Eclipse. Vous trouverez au chapitre Produits supplémentaires quelques manipulations de base à connaître pour configurer JBoss, importer les projets et les déployer.

1. Exemple de déploiement d’un EJB

Un EJB est archivé dans un fichier dont l’extension est jar. La structure de ce fichier est la suivante :

les fichiers de classes qui composent l’EJB, avec la structure du package ;

un répertoire META­INF qui comporte les fichiers de déploiement ejb­jar.xml et jboss.xml.

Si nous ouvrons l’archive, nous trouvons les répertoires suivants :

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzPyIko6KRzIgLAA==-enidentnumber

Nous y retrouvons le package fr.eni.editions.jboss.ejb2.calculette et le répertoire META­INF qui contient les fichiers de déploiement ejb­jar.xml et jboss.xml.

Ce dernier fichier contient le nom JNDI, sous lequel sera enregistré le composant.

<?xml version=’1.0’ encoding=’UTF-8’ ?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.2//EN" "http://www.jboss.org/j2ee/dtd/jboss_4_2.dtd"> <jboss> <enterprise-beans> <session> <ejb-name>Calculette</ejb-name> <jndi-name>ejb2/calculette/remote</jndi-name> <local-jndi-name> ejb2/calculette/local </local-jndi-name> </session> </enterprise-beans> </jboss>

La balise <ejb-name> reprend le nom de l’EJB déclaré dans le fichier ejb­jar. Les balises <jndi-name> et <local-jndi-name> sont les noms JNDI, sous lesquels les interfaces ’local’ et ’remote’ seront enregistrées.

Le déploiement du fichier jar est automatique, il suffit de le copier dans le répertoire deploy. La console permettra de suivre la prise en compte de l’EJB.

De plus, vous pouvez contrôler les noms JNDI dans la console d’administration. Saisissez l’url http://localhost:8080/ dans un navigateur et suivez le lien JMX Console. Vous vous trouvez en présence de la page qui recense les services de JBoss. Nous présenterons plus en détail les services JMX (Java management eXtension) de JBoss.

Recherchez dans cette page, l’entrée qui correspond au service d’affichage des noms JNDI : service=JNDIView.

Cliquez sur ce lien. Dans la vue suivante, recherchez l’opération list et cliquez sur le bouton Invoke.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzPyIko6KRzIgLAA==-enidentnumber

La page affiche maintenant l’arborescence des noms JNDI. À la fin de la page, dans la rubrique Global JNDI Namespace, vous retrouverez les noms JNDI de votre EJB.

+- ejb2 (class: org.jnp.interfaces.NamingContext) | +- calculette (class: org.jnp.interfaces.NamingContext) | | +- local (proxy: $Proxy58 implements interface fr.eni.editions.jboss.ejb2.calculette.CalculetteLocalHome) | | +- remote (proxy: $Proxy60 implements interface fr.eni.editions.jboss.ejb2.calculette.CalculetteHome,interface javax.ejb.Handle)

Le déploiement s’est bien passé.

2. Exemple de déploiement d’une application Web

L’application web est archivée dans un fichier dont l’extension est war. De la même manière que pour l’EJB, il suffit de copier ce fichier dans le répertoire deploy pour que l’application soit déployée sur le serveur JBoss.

La structure d’un fichier war est la suivante :

Le descripteur de déploiement jboss­web.xml permet de fixer l’url d’appel de l’application Web. Ainsi dans notre cas l’application web sera accessible à l’adresse : http://localhost:8080/cwc.

... <jboss-web> <context-root>cwc</context-root>

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzPyIko6KRzIgLAA==-enidentnumber

</jboss-web>

Dans la console, vous pouvez suivre le déploiement de l’application Web.

10:29:45,375 INFO [TomcatDeployer] deploy, ctxPath=/cwc,

warUrl=.../tmp/deploy/tmp60456Chap 2 - Client web calculette EJB2-exp.war/

10:29:55,781 INFO [EjbModule] Deploying Calculette 10:29:55,890 INFO [BaseLocalProxyFactory] Bound EJB LocalHome ’Calculette’ to jndi ’ejb2/calculette/local’ 10:29:55,890 INFO [ProxyFactory] Bound EJB Home ’Calculette’ to jndi ’ejb2/calculette/remote’ 10:29:55,890 INFO [EJBDeployer] Deployed: file:/C:/SERVEURS/ jboss-4.2.2.GA/server/default/deploy/Chap 2 - Calculette EJB2.jar

Si l’EJB a été précédemment déployé, vous pouvez tester l’application Web et vous devriez obtenir l’écran suivant.

3. Exemple de déploiement d’une application d’entreprise

Les applications d’entreprise regroupent plusieurs modules EJB, Web, bibliothèques... au sein d’une archive dont l’extension est ear.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzPyIko6KRzIgLAA==-enidentnumber

Il suffit de copier ce fichier dans le répertoire deploy pour que les différents modules soient pris en compte par le serveur JBoss.

4. Retrait d’une application

Pour supprimer une application, il suffit d’enlever du répertoire deploy le fichier archive. La console vous permettra de suivre le retrait de l’application ou de l’EJB qui est en cours de suppression.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzPyIko6KRzIgLAA==-enidentnumber

Noyau du serveur

Afin de permettre un développement modulaire, JBoss est composé de services qui s’ajoutent à un noyau, comme des plugins. Cette modularité est basée sur la spécification JMX (Java Management eXtension). Après une présentation de la spécification JMX, nous verrons comment celle­ci est implémentée sous JBoss, et comment nous pouvons gérer les services JBoss et ajouter nos propres services.

1. Présentation de la spécification JMX

La spécification JMX fournit un standard pour gérer et superviser des ressources matérielles et logicielles, y compris la machine virtuelle. Cette spécification fait partie de la plate­forme standard (JSE) depuis la version 5.

L’instrumentation des ressources s’effectue au moyen de composants spécifiques appelés MBean (Managed Bean).

L’architecture JMX comporte 3 niveaux :

un niveau "Instrumentation" avec les MBeans, où le MBean peut être perçu comme une "sonde" permettant de gérer une ressource. Le MBean instrumentalise la ressource à superviser ;

un niveau "Agent" avec le conteneur de MBeans (ou serveur de MBeans) qui gère les MBeans et peut exécuter des opérations sur ces MBeans ;

un niveau "Distribution" constitué des mécanismes de communication entre les applications de supervision et le niveau "Agent". La plate­forme Java inclut une console de supervision des MBeans : jconsole qui se trouve dans le sous­répertoire bin du répertoire d’installation du JDK.

Le MBean doit suivre un modèle de conception qui consiste à avoir :

des attributs en lecture et ou écriture ;

des opérations ;

une description.

La spécification définit 5 types de MBeans : standard, dynamique, ouvert, modèle et MXBean. Nous ne présenterons ici que le MBean standard, l’objectif étant de comprendre la prise en compte et la gestion des services par JBoss.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

Un MBean standard est composé :

d’une interface nommée QuelqueChoseMBean :

les attributs sont exposés par des getters/setters.

les opérations sont des méthodes publiques.

d’une classe nommée QuelqueChose qui implémente l’interface QuelqueChoseMBean.

Les exemples suivants sont extraits du package fr.editions.eni.jboss.mbeans du projet "Chap 3 ­ MBean" de l’archive téléchargée. Cet exemple est trivial mais utile pour illustrer les mécanismes de base de JMX.

a. La couche instrumentation : l’interface HelloMBean

Elle possède :

deux attributs : Name en lecture seule, et Couleur en lecture et écriture.

deux opérations sayHello() et additionner(…).

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

package fr.editions.eni.jboss.mbeans;

public interface HelloMBean

// attributs public String getName();

public void setCouleur(String couleur);

public String getCouleur();

// opérations public void sayHello();

public double additionner(double a, double b);

b. La couche instrumentation : implémentation du MBean

Cette implémentation est triviale et n’appelle pas de commentaires.

package fr.editions.eni.jboss.mbeans;

public class Hello implements HelloMBean

private String couleur = "vert";

private String name = "ENI";

public double additionner(double a, double b)

return a+b;

public String getCouleur()

return couleur;

public String getName()

return name;

public void sayHello()

System.out.println("Bonjour tout le monde");

public void setCouleur(String couleur)

this.couleur = couleur;

c. La couche Agent

JSE fournit un conteneur de MBean que nous pouvons utiliser pour y enregistrer notre MBean.

public class AgentHelloMBean

public static void main(String[] args) throws Exception

// Récupération d’un MBean Serveur MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

// Tout MBean doit être associé à un ObjectName ObjectName on = new

ObjectName("fr.editions.eni.jboss.mbeans:type=Hello"); // Instansiation de Hello, puis enregistrement dans le serveur Hello hello = new Hello();

mbs.registerMBean(hello, on); // Attente System.out.println("Serveur démarré...");

Thread.sleep(Long.MAX_VALUE);

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

d. La couche distribuée : jconsole

Pour tester cet exemple, exécutez la classe AgentHelloMBean. Puis, démarrez jconsole, qui est un exécutable qui se trouve dans le répertoire bin de votre JDK (avec java, javac…). Nous utilisons ici le JSE 6.

Si l’agent est bien démarré, vous devez le voir apparaître dans la fenêtre de connexion.

Sélectionnez fr.editions.eni.jboss.mbeans.AgentHelloMBean et cliquez sur le bouton Connect. Puis sélectionnez l’onglet MBeans.

Vous retrouvez les deux attributs Couleur et Name, notez que ces attributs commencent par une majuscule. Vous pouvez changer la valeur de Couleur, mais pas celle de Name.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

En sélectionnant l’opération additionner, vous pouvez entrer les paramètres p1 et p2, puis cliquer sur le bouton additionner. Le résultat apparaît dans une fenêtre popup.

Pour l’opération sayHello, le résultat apparaît dans la fenêtre d’exécution de la classe AgentHelloMBean.

e. Supervision : notification d’événement

JMX définit un mécanisme de notification, qui permet d’être à l’écoute d’un changement d’état, d’une anomalie qui

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

survient…

Le MBean doit implémenter l’interface NotificationEmitter, ou étendre la classe NotificationBroadcastSupport.

Dans notre exemple, nous allons réagir à un changement de l’attribut Couleur. Un client pourra alors s’inscrire auprès du MBean, via un listener, pour être notifié, lors du changement de la valeur de l’attribut.

Les extraits suivants de code sont situés dans le package fr.editions.eni.jboss.mbeans.notification du projet "Chap 3 ­ MBean".

La classe Hello hérite maintenant de NotificationBroadcastSupport. L’implémentation de la méthode setCouleur de la classe Hello a changé. Une notification est instanciée puis elle est ensuite envoyée. Si un client est à l’écoute de cette notification, il pourra réagir au changement d’état.

Avant de poursuivre, veillez à arrêter la classe AgentHelloMBean précédente.

public synchronized void setCouleur(String couleur)

String oldCouleur = this.couleur;

this.couleur = couleur;

System.out.println("=> Changement de couleur. Passage de

"+oldCouleur+" à "+this.couleur);

Notification n = new

AttributeChangeNotification(this,sequenceNumber++,

System.currentTimeMillis(), "CHANGEMENT DE COULEUR", "couleur","String",oldCouleur,this.couleur);

sendNotification(n);

Comme précédemment, exécutez la classe AgentHelloMBean et connectez la console jconsole sur cet agent.

Cliquez sur l’onglet MBeans, dans le navigateur de MBeans, vous devriez maintenant voir un nouvel item Notifications.

Cliquez sur cet item, dans la partie droite de la fenêtre, l’historique des notifications s’affiche avec les boutons :

Subscribe pour s’inscrire auprès du MBean pour être à l’écoute d’une notification ;

Unsubscribe pour se désinscrire ;

Clear pour effacer l’historique des notifications reçues.

Cliquez sur Subscribe et changez la valeur de l’attribut Couleur.

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

La notification du changement de la valeur de l’attribut a bien été prise en compte.

2. Implémentation de JMX dans JBoss

JBoss utilise la spécification JMX comme un "bus logiciel", ce qui permet d’ajouter des composants JMX. Ceux­ci sont les services JBoss.

JBoss utilise JMX comme micro­noyau.

3. Processus de démarrage de JBoss

Lors du démarrage de JBoss, par la commande run, une instance de serveur de MBeans est créée. Cette instance joue le rôle de micro­noyau. Ce micro­noyau ne fournit aucune fonctionnalité à JBoss, si ce n’est celle d’ajouter des services sous forme de MBeans.

L’ensemble du processus de démarrage s’affiche dans la console dans laquelle le script de démarrage est exécuté.

Les grandes étapes du démarrage du serveur sont :

création d’un MBeanserver ;

enregistrement des MBeans ServerImpl et ServerConfigImpl ;

initialisation du chargeur de classes (class loader) ;

création du MBean org.jboss.system.ServiceController qui contrôle le cycle de vie des MBeans de JBoss ;

démarrage des services de gestion de déploiement ;

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

chargement des services décrits dans le fichier jboss­service.xml.

4. Le fichier jboss­service.xml

Avant toute manipulation de ce fichier, créez une copie du fichier original.

Lors du démarrage du micro­noyau, le fichier jboss­service.xml est lu pour activer les services de base. Les services décrits dans ce fichier de configuration ont la durée de vie de l’exécution du serveur. Ce fichier de configuration est situé dans le répertoire conf de la configuration serveur : minimal, all, ou default. Dans les répertoires de serveur all et default, il y a un fichier jboss­minimal.xml, de plus, non utilisé, qui indique la configuration minimum pour que JBoss puisse démarrer. Il faut noter que cette configuration minimum n’est pas compatible avec les spécifications JEE.

Ce fichier est le fichier de configuration principal des MBeans du serveur JBoss. Chaque MBean possède son propre fichier de configuration jboss­service.xml.

La structure du fichier jboss­service.xml est très simple. Vous pouvez ouvrir le fichier de configuration de la configuration serveur par défaut : <répertoire JBoss>/server/default/conf/jboss­service.xml.

<server> <classpath …/> <mbean …> <attribute …>…</attribute> </mbean … </server>

La valeur des attributs des éléments du fichier de configuration n’est pas nécessairement codée en dur. Les attributs peuvent aussi prendre la valeur des propriétés système sous la forme $nom_propriété.

<server>

Racine du fichier XML. Cet élément contient les éléments <classpath> et <mbean>.

<classpath>

Cet élément spécifie les fichiers JAR qui doivent être déployés avec les Mbeans.

L’attribut codebase de l’élément <classpath> indique quel répertoire est utilisé. Il contient ici la valeur $jboss.server.lib.url:lib. Celle­ci est une propriété utilisée par la classe org.jboss.system.server.ServerConfig qui pointe, par défaut, vers le répertoire lib de la configuration serveur.

L’attribut archives peut spécifier les fichiers JAR à charger. Un astérisque "*" indique que tous les fichiers JAR seront chargés.

<mbean>

Cet élément spécifie le service MBean qui sera chargé.

L’attribut code indique le nom complètement qualifié, qui contient le nom de la classe et son paquetage, de la classe d’implémentation du Mbean.

L’attribut name spécifie le nom JMX du Mbean.

L’éventuel attribut xmbean-dd spécifie le répertoire des ressources utilisées par le service MBean, si celui­ci utilise le descripteur de XMbean de JBoss.

L’élément <mbean> contient lui­même d’autres éléments XML : <attribute>, <depends>.

<attribute>

Chaque élément attribut spécifie une paire name/valeur d’un attribut du Mbean.

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

L’attribut name de l’élément correspond au nom de l’attribut du Mbean.

La valeur du corps de l’élément correspondra à la valeur de l’attribut.

Le corps de l’élément <attribute> peut aussi être constitué d’autres balises filles. Ces balises filles correspondent à des attributs du MBean qui doivent être de type org.w3c.dom.Element.

<depends>

Cet élément spécifie une dépendance par rapport à un autre Mbean.

L’attribut optionnel optional-attribute-name spécifie le nom de l’attribut du MBean qui recevra, par injection, une référence à l’instance de type javax.management.ObjectName du MBean, spécifié dans le corps de l’élément <depends>.

Le corps de l’élément contient le nom JMX du MBean.

Dans le fichier de configuration du serveur par défaut, nous retrouvons un MBean qui correspond à la spécification JSR77. Cette spécification définit un modèle d’information pour administrer le serveur par des outils tiers partie. Le serveur doit exposer ce modèle d’information. Le modèle reprend les types d’objets faisant partie d’un serveur d’application JEE. Ces objets sont appelés objets administrés et sont des abstractions d’une ressource comme le service Mail par exemple.

JSR77 repose sur le standard JMX.

Nous trouvons dans ce fichier de configuration, le service qui s’occupe du déploiement à chaud. En fin du fichier, vous trouverez l’élément lié à ce MBean.

<mbean code="org.jboss.deployment.scanner.URLDeploymentScanner" name="jboss.deployment:type=DeploymentScanner,flavor=URL">

Ce MBean servira lors de la présentation de la spécification JMX pour vous montrer les différentes manières de le gérer.

Nous allons voir ici quelques attributs de ce MBean, et leur effet sur le déploiement à chaud.

<attribute name="ScanEnabled">true</attribute>

Si ScanEnabled est mis à false, le déploiement à chaud n’est plus effectif. Cela peut être intéressant dans une configuration en production, pour des raisons de sécurité.

<attribute name="ScanPeriod">5000</attribute>

ScanPeriod représente le temps entre deux explorations des répertoires de déploiement à chaud, en millisecondes. Ce temps peut être raccourci lorsque le serveur est sur une machine de développement, pour que le développeur ne s’impatiente pas lors des redéploiements.

<attribute name="URLs"> deploy/ </attribute>

URLs représente la liste des URLs participant au déploiement à chaud. Ce sont ces URLs qui seront explorées pour charger les composants. La virgule "," est le séparateur d’URLs. Si l’URL finit par un slash "/", elle est considérée comme un répertoire et donc contient une collection de composants pouvant être déployés. Un URL comme deploy/ indique qu’il s’agit d’un répertoire, situé relativement au répertoire du serveur démarré (serveur par défaut dans notre exemple) : <repertoire_jboss>/server/default/deploy/.

Les URL absolues doivent être préfixées par file:// s’il s’agit d’un système de fichier, ou http:// s’il s’agit d’un déploiement à chaud distant.

Pour ajouter un répertoire situé sur la même machine que le serveur, mais en dehors du répertoire de la configuration serveur de démarrage, la syntaxe sera la suivante :

<attribute name="URLs"> deploy/,c:/deploiements/jboss/ </attribute>

Nous pouvons aussi indiquer si l’exploration des répertoires de déploiement doit être effectuée de manière récursive.

<attribute name="RecursiveSearch">True</attribute>

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

Si l’attribut RecursiveSearch est mis à true, alors les sous­répertoires des répertoires de déploiement seront aussi pris en compte.

5. Propriétés utilisables dans les fichiers de configuration JBoss

Vous pouvez utiliser un certain nombre de propriétés comme valeur d’attribut des fichiers XML de JBoss. L’utilisation de ces propriétés permet de ne pas être tributaire du répertoire d’installation de JBoss. La syntaxe est alors la suivante :

<balise attribut="$propriété" ...>

La propriété sera évaluée comme une chaîne de caractères, vous pouvez concaténer vos propres chaînes de la manière suivante :

<balise attribut="$propriétéma_chaine" ...>

Par exemple :

keystoreFile="$jboss.server.home.dir/conf/eni.keystore"

Les propriétés sont classées par type.

a. Propriétés de démarrage

jboss.boot.library.list : liste des archives utilisées lors du démarrage du serveur, par défaut : log4j­boot.jar,jboss­common.jar,jboss­system.jar.

jboss.server.type : nom du serveur actif, par défaut : default.

jboss.server.root.deployment.filename : fichier à déployer à la fin du processus de démarrage, par défaut : jboss­service.xml.

b. Propriétés représentant des URLs ou des répertoires

jboss.home.dir : répertoire d’installation de la distribution JBoss, par défaut la variable d’environnement $JBOSS_HOME.

jboss.home.url : URL d’installation de JBoss, par défaut, la variable d’environnement $JBOSS_HOME.

jboss.lib.url : archives du noyau, par défaut : $jboss.home.url/lib.

jboss.server.name : nom du serveur actif, par défaut : default.

jboss.server.base.dir : répertoire où se trouve les configurations serveur, par défaut : $jboss.home.dir/server.

jboss.server.base.url : URL où se trouve les configurations serveur, par défaut : $jboss.home.dir/server.

jboss.server.home.dir : répertoire de la configuration courante, par défaut : $jboss.server.base.dir/$jboss.server.name.

jboss.server.home.url : URL de la configuration courante, par défaut : $jboss.server.base.url/$jboss.server.name.

jboss.server.temp.dir : répertoire des fichiers temporaires, par défaut : $jboss.server.home.dir/tmp.

jboss.server.data.dir : répertoire des fichiers de données, par défaut : $jboss.server.home.dir/data.

jboss.server.config.url : URL des fichiers de configuration, par défaut : jboss.server.home.url/conf.

jboss.server.lib.url : URL du répertoire des librairies du serveur, par défaut : $jboss.server.home.url/lib.

jboss.server.log.dir : répertoire des fichiers de log, par défaut : $jboss.server.home.dir/log.

c. Propriétés de configuration

jboss.bind.address : nom de l’hôte, ou adresse IP d’écoute du serveur, par défaut : 0.0.0.0 = "ANY" NIC ­ (à partir de v4.2.*) 127.0.0.1 = localhost seulement.

La liste complète des propriétés peut être trouvée sur : http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossProperties

- 10 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

6. Connexion au serveur JMX de JBoss

La connexion au serveur JMX s’effectue via des adaptateurs. JBoss fournit des adaptateurs pour des connexions HTTP, RMI et EJB.

a. Connexion par la console web : JMX Agent View

Dans un navigateur, lancez une connexion sur l’URL http://localhost:8080/jmx­console.

La vue principale correspond à la vue "agent". Elle liste tous les MBeans enregistrés sur le serveur et triés par domaines auxquels ils appartiennent.

La sélection d’un lien ouvre la vue sur le MBean. Par exemple, recherchez le MBean DeploymentScanner, correspondant à la liste des objets référencés dans l’annuaire JNDI du serveur. Dans le domaine jboss.deployement, recherchez le lien sur le DeploymentScanner, puis cliquez dessus.

Une nouvelle page affiche les caractéristiques du MBean : les attributs du MBean et les opérations disponibles. Le MBean que nous utilisons dans cet exemple est celui qui gère les répertoires de déploiement. Nous allons ajouter un nouveau répertoire de déploiement dynamiquement, sans passer par le fichier jboss­service.xml.

- 11 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

Sur cet écran, nous retrouvons le nom des attributs (qui correspondent aux noms des méthodes set/get du MBean), leur type, leur accès en ecriture (W) et/ou lecture (R) (présence du setter et/ou getter), leur valeur, ainsi qu’une description.

Les attributs en écriture peuvent être modifiés directement dans le formulaire, si leur type est compatible avec une saisie, les modifications étant prises en compte après un clic sur le bouton Apply Changes.

L’attribut URLList présente toutes les URLs susceptibles de servir de répertoire de déploiement. Pour l’instant, vous ne devriez avoir qu’un répertoire de déploiement dans cette liste, du type file:/<répertoire JBoss>/server/default/deploy.

Avant d’ajouter le nouveau répertoire de déploiement, il faut d’abord le créer. Dans l’exemple suivant, ce répertoire s’appelle test et est créé dans :/<répertoire JBoss>/server/default. La saisie directe dans le formulaire n’est pas ici possible car l’attribut est de type List, incompatible avec un mode de saisie directe. Nous allons l’ajouter par le biais d’une opération.

La liste des opérations disponibles est située sous la liste des attributs du MBean. Recherchez l’opération addURL().

Cet écran nous donne :

le nom de l’opération, ici addURL() ;

le type de retour de l’opération, ici void, donc sans retour ;

la liste des paramètres avec leur type, une zone de saisie et une éventuelle description, ici un seul paramètre p1, de type String ;

un bouton Invoke permettant d’invoquer l’opération.

Dans la zone de saisie, ajoutez votre répertoire en respectant les conventions vues précédemment, puis cliquez sur le bouton Invoke. Si le répertoire a été correctement ajouté. vous devriez avoir un écran tel que celui­ci :

Si l’opération nous avait retourné une valeur, cette valeur serait affichée. Si une erreur est survenue, la page affiche

- 12 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

alors la trace de l’exception qui a été levée.

Vous pouvez contrôler maintenant que le répertoire a été ajouté en cliquant sur le lien Back to MBean View et en vérifiant la valeur de l’attribut URLList. Celui­ci doit maintenant contenir deux répertoires.

b. Connexion par la console web : sécurisation

Étant donné l’importance du rôle de cette console HTML, l’accès peut en être sécurisé. Le site web de la console correspond au répertoire <répertoire JBoss>/server/default/deploy/jmx­console.war. Les arborescences classiques d’une application JEE de type web s’y retrouvent.

Les contraintes de sécurité sont en commentaire dans les fichiers web.xml et jboss­web.xml. Il suffit de décommenter les éléments <security-constraint> du fichier web.xml, et <security-domain> du fichier jboss­web.xml. Les fichiers des profils (jmx­console­roles.properties) et des utilisateurs (jmx­console­users.properties) se trouvent dans le répertoire <répertoire JBoss>/server/default/conf/props, l’utilisateur par défaut est admin avec le mot de passe admin. Redémarrez le serveur si nécessaire.

Une fenêtre de saisie de l’utilisateur et de son mot de passe doit s’afficher :

Ce chapitre ne détaille pas les mécanismes mis en œuvre et comment les modifier. Les spécifications des applications web seront vues au chapitre Déploiement d’application Web et la sécurisation des applications

web sera détaillée dans le chapitre Gestion de la sécurité.

c. Connexion par l’outil twiddle

Dans le sous­répertoire bin du répertoire d’installation de JBoss, vous trouverez un outil nommé twiddle. Cet outil permet de se connecter au serveur JMX de JBoss en ligne de commande. C’est un script livré aux formats Unix (twiddle.sh) et Windows (twiddle.bat).

Le format général de l’utilisation de twiddle est :

twiddle [option] <command> [arguments de la commande]

Pour obtenir une aide générale, tapez twiddle -h :

A JMX client to ’twiddle’ with a remote JBoss server. usage: twiddle [options] <command> [command_arguments] options: -h, --help Show this help message --help-commands Show a list of commands -H=<command> Show command specific help -c=command.properties Specify the command.properties file to use -D<name>[=<value>] Set a system property -- Stop processing options -s, --server=<url> The JNDI URL of the remote server -a, --adapter=<name> The JNDI name of the RMI adapter to use -u, --user=<name> Specify the username for authentication -p, --password=<name> Specify the password for authentication -q, --quiet Be somewhat more quiet

Pour obtenir la liste des commandes, tapez twiddle -help-commands :

- 13 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

twiddle commands: jsr77 Print out JSR77 related information xmbean Print out mbean metadata as an xmbean descriptor info Get the metadata for an Mbean get Get the values of one or more MBean attributes invoke Invoke an operation on an Mbean create Create an Mbean setattrs Set the values of one or more MBean attributes unregister Unregister one or more Mbeans query Query the server for a list of matching Mbeans set Set the value of one MBean attribute serverinfo Get information about the MBean server

Pour obtenir l’aide sur une commande particulière, la commande invoke par exemple, tapez :

twiddle -H=invoke

Help for command: ’invoke’ Invoke an operation on an Mbean usage: invoke [options] <query> <operation> (<arg>)* options: -q, --query-type[=<type>] Treat object name as a query -- Stop processing options query type: f[irst] Only invoke on the first matching name [default] a[ll] Invoke on all matching names

Par twiddle, nous pouvons obtenir l’état d’un attribut d’un MBean. Par exemple pour obtenir la liste des URLs de déploiement :

twiddle get "jboss.deployment:flavor=URL,type=DeploymentScanner" URLList

Sous Ubuntu, les caractères guillemets " doivent être remplacés par des apostrophes ’.

C:\SERVEURS\jboss-4.2.2.GA\bin>twiddle get "jboss.deployment:flavor=URL, type=DeploymentScanner" URLList URLList=[file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/deploy/] C:\SERVEURS\jboss-4.2.2.GA\bin>

Par la commande invoke nous pouvons ajouter un répertoire de déploiement.

twiddle invoke "jboss.deployment:flavor=URL,type=DeploymentScanner" addUrl "file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/test/"

Cette commande ne renvoie pas de résultat, aussi un retour ’null’ apparaît sur la console. Vous pouvez maintenant lancer une commande get pour vérifier que l’URL a été prise en compte.

C:\SERVEURS\jboss-4.2.2.GA\bin>twiddle invoke "jboss.deployment:flavor=URL, type=DeploymentScanner" addURL "file:/C:/SERV EURS/jboss-4.2.2.GA/server/default/test" ’null’ C:\SERVEURS\jboss-4.2.2.GA\bin>twiddle get "jboss.deployment:flavor=URL, type=DeploymentScanner" URLList URLList=[file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/deploy/, file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/test, file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/test]

L’utilitaire twiddle se connecte par défaut en localhost (127.0.0.1). Pour préciser une machine distante, il faut lancer la commande avec l’option ­s :

twiddle -s 192.168.2.104 serverinfo -c

De même si plusieurs instances de JBoss sont exécutées sur une même machine, il faut préciser le port de connexion (qui est 1099 par défaut) :

- 14 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

twiddle -s 127.0.0.1:1199 serverinfo -c

d. Connexion par RMI

Une application java peut se connecter au serveur JMX de JBoss via l’interface org.jboss.jmx.adaptor.rmi.RMIAdaptor, qui est liée dans le système de nommage JNDI au nom jmx/invoker/RMIAdaptor.

La section suivante détaillera les aspects JNDI.

Les codes présentés sont extraits de la classe URLDeploymentClient du projet "Chap 3 ­ Client JMX JBoss".

InitialContext ic = new InitialContext();

RMIAdaptor server =(RMIAdaptor)ic.lookup("jmx/invoker/RMIAdaptor"); ObjectName name = new

ObjectName("jboss.deployment:flavor=URL,type=DeploymentScanner");

Les deux premières lignes de code permettent de récupérer auprès de JNDI, un RMIAdaptor, vers le serveur de MBean. Il faut veiller à avoir un fichier jndi.properties adéquat dans votre classpath (cf. section suivante).

La ligne suivante permet de construire un ObjectName sur le domaine et les clés du MBean. Le MBean est aussi enregistré dans le serveur de MBeans.

System.out.println("\nListe des URLS avant modification ");

List<URL> urls = (List<URL>)server.getAttribute(name, "URLList"); for(URL url : urls)

System.out.println(">> "+url);

L’attribut URLList nous renvoie une liste d’objets de type URL. L’instruction server.getAttribute(name, "URLList") permet de récupérer cette liste en fournissant au serveur le ObjectName du MBean et le nom de l’attribut.

L’invocation des méthodes est un peu plus complexe, car il faut renseigner :

le nom de la méthode ;

les types de paramètres attendus sous forme de tableau de chaînes de caractères. Attention, les classes doivent être complètement qualifiées pour pouvoir être utilisées.

les valeurs des paramètres, sous forme de tableau d’Object.

String newUrl = "test/"; String[] signature = "java.lang.String"; Object[] parametres = newUrl; server.invoke(name, "addURL", parametres, signature);

La variable newUrl correspond au répertoire que nous avons créé précédemment.

L’invocation de l’opération se fait via le serveur, en précisant le nom du MBean, le nom de l’opération, le tableau des paramètres ainsi que celui des types des paramètres.

Il suffit de réafficher la valeur de l’attribut URLList pour vérifier la prise en compte des changements.

À l’exécution de la méthode main(…) de la classe URLDeploymentClient, l’affichage suivant est produit :

Liste des URLS avant modification >> file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/deploy/ Liste des URLS après modification >> file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/deploy/ >> file:/C:/SERVEURS/jboss-4.2.2.GA/server/default/test/

- 15 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZy7uxKKRzIgLAA==-enidentnumber

Service de nommage JNDI

1. Présentation de JNDI

Dans une architecture distribuée, les ressources doivent être retrouvées avant d’être utilisées. Il faut donc un service qui centralise la localisation des ressources et associe à chacune d’entre elles une identification unique.

Il existe de nombreuses utilisations de ce type d’association :

une ressource de type fichier est associé à son nom au sein du système de fichiers (FS, pour File System) ;

sur Internet les noms de machines (www.eni.fr) sont associées à des adresses IP (192.78.45.48 par exemple) par les DNS (Domain Name Service) ;

des objets sont associés à une chaîne de caractères pour être utilisés par RMI.

JNDI (Java Naming and Directory Interface) permet d’associer une ressource avec un nom, c’est un annuaire de nommage. JNDI est une API contenant un ensemble d’interfaces qui permet d’utiliser tout type d’annuaire : LDAP, RMI, DNS, CORBA, système de fichiers…

Une liste de fournisseurs de services JNDI est maintenue par Sun et disponible à l’URL http://java.sun.com/products/jndi/serviceproviders.html.

Pour bien comprendre les concepts des systèmes de nommage, il est nécessaire de distinguer les notions de nom (name), association (binding) et contexte.

a. Les noms

La notion de nom est fondamentale dans JNDI. La syntaxe du nom est déterminée par le système de nommage utilisé, c’est une convention de nommage.

Par exemple, dans un système de fichier Unix, le nom de chaque fichier est décrit par une liste de répertoires séparés par le caractère "/". Par exemple, /usr/jboss/readme.txt correspond au fichier readme.txt situé le répertoire jboss du répertoire usr, situé sous la racine du système de fichiers.

Pour un DNS, un nom est composé de chaînes de caractères, séparées par des points "." : www.eni.editions.fr, par exemple.

b. Les noms atomiques

C’est une partie indivisible du nom. Par exemple www, eni, editions, fr sont des noms atomiques par rapport à www.eni.editions.fr.

c. Les associations

Un objet est associé à un nom. En fonction du système de nommage, c’est directement l’objet qui est associé, ou bien une référence vers cet objet. Dans un contexte de serveur d’applications, les références des objets sont associées aux noms, bien que pour simplifier nous parlerons d’objet. Dans JNDI, une association est représentée par l’interface Binding.

d. Le contexte

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

Le contexte est un objet contenant des associations objet ­ nom. L’utilisation de JNDI commence par la récupération d’un contexte de système de nommage. L’interface javax.naming.Context est implémentée par la classe javax.naming.InitialContext, qui est le point d’entrée de l’utilisation de JNDI.

Un contexte initial est créé à partir :

des propriétés du paramètre d’environnement passé au constructeur ;

d’un fichier jndi.properties visible dans le classpath.

Une erreur fréquente est d’utiliser un fichier jndi.properties par défaut ou inapproprié. Dans ce cas, une exception de type javax.naming.NoInitialContextException est levée.

package fr.eni.editions.exemples.jndi;

import javax.naming.*;

import java.util.*;

public class TestJndiFs

public static void main(String[] args) throws Exception

Hashtable<String,String> env = new

Hashtable<String,String>(); // Mise en place des propriétés // pour le contexte initial env.put("java.naming.factory.initial", "com.sun.jndi.fscontext.RefFSContextFactory"); env.put("java.naming.provider.url", "file:/c://SERVEURS"); Context ctx = new InitialContext(env);

NamingEnumeration<NameClassPair> liste= ctx.list("//jboss-4.2.2.GA"); while(liste.hasMore())

System.out.println(liste.next().getName());

Cet exemple permet l’affichage des ressources situées sous le répertoire C:\SERVEURS. Il faudra adapter la propriété java.naming.provider.url à votre machine. Le système de fichiers (FS) est ici vu comme un système de nommage et utilisé via JNDI.

Les principales méthodes de la classe Context sont :

bind

Ajoute une entrée nom ­ objet dans l’arborescence du service de nommage.

rebind

Modifie une entrée du service de nommage, ou l’ajoute si elle n’existe pas.

listBindings

Retourne les objets de l’arborescence sous forme de NamingEnumeration, avec les objets attachés.

list

Retourne une énumération des objets sous forme de NameClassPair, sans les objets attachés.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

lookup

Méthode de recherche d’un objet en fonction de son nom, dans le service de nommage.

Les librairies java nécessaires sont disponibles dans le répertoire librairies de l’archive téléchargeable des exemples. Ces librairies, sous forme de fichiers jar, doivent être accessibles à l’exécution. Il faut les associer avec la variable système classpath.

Ces librairies sont bien sûr téléchargeables sur le site de Sun, via l’URL : http://java.sun.com/products/jndi/downloads/index.html.

puis, sur le lien : Download JNDI 1.2.1 & More.

et enfin, sur le lien : File System Service Provider, 1.2 Beta 3.

Chaque composant EJB est référencé par le serveur d’application dans le service de nommage.

Une application cliente qui veut utiliser ce composant doit d’abord se connecter au service de nommage par JNDI, puis récupérer le composant en indiquant son nom :

InitialContext ctx = new InitialContext();

calculette=(CalculetteRemote)ctx.lookup("ejb3/Calculette/remote");

La prise en compte du contexte initial est différente suivant la position de l’objet client : dans le serveur d’application JBoss, ou dans une application type client lourd.

Par exemple, si le client Web de l’EJB est déployé dans le même serveur d’application que le composant EJB, il n’est pas nécessaire de donner un environnement particulier au constructeur de InitialContext. Il suffit d’utiliser le constructeur par défaut.

S’il s’agit d’un client lourd, déployé en dehors du serveur d’application, il faut utiliser le constructeur par défaut, de la classe InitialContext et un fichier jndi.properties approprié, tel que celui­ci :

### JBossNS properties java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=jnp://localhost:1099 java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces

Dans ce fichier, les lignes commençant par un caractère dièse (#) sont des lignes de commentaires. java.naming.provider.url est l’adresse du port d’écoute du service de nommage. Dans cet exemple, le service de nommage est sur la même machine que l’application cliente, et écoute sur le port 1099. Dans le chapitre Déploiement des EJB, nous verrons comment inclure les librairies du service dans notre application distante. Vous remarquerez que les classes utilisées sont spécifiques à JBoss. Chaque serveur d’application utilise sa propre implémentation de service de nommage. Il est donc préférable de mettre les propriétés du service de nommage dans un fichier, plutôt que dans un javax.util.Hashtable passé au constructeur de InitialContext.

Parfois, il est nécessaire de travailler via HTTP pour passer à travers les proxy et firewall. Il existe des propriétés permettant à un client distant de réaliser cet accès vers JNDI, via HTTP. Les propriétés du fichier jndi.properties deviennent alors :

java.naming.factory.initial=org.jboss.naming.HttpNamingContextFactory java.naming.provider.url=jnp://localhost:8080/invoker/JNDIFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces

Le code du client distant reste le même.

Le client distant doit avoir dans son classpath les classes JBoss nécessaires. L’ensemble des packages se trouve dans le sous­répertoire client du répertoire d’installation de JBoss. En général, l’utilisation du

package jbossall-client.jar est suffisant.

2. L’ENC (Environment Naming Context)

Une application distribuée est basée sur l’assemblage de composants EJB. JNDI va permettre la recherche par leur nom, des composants, et des ressources, comme les sources de données, au sein du serveur. Les applications se réfèrent à leur propre contexte de nommage, l’ENC (Environment Naming Context). Ce contexte est accessible sous le nom java:comp/env.

Context initCtx = new InitialContext(); Context ctx = (Context) initCtx.lookup("java:comp/env");

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

Nous verrons ultérieurement que les descripteurs de déploiement du composant permettent de paramètrer les entrées ENC.

a. Niveau de visibilité des contextes JNDI sous JBoss

Selon le contexte utilisé, la ressource sera accessible seulement au sein de l’application, du serveur, ou en dehors du serveur. L’ordre de présentation des contextes est l’inverse de celui de la visualisation par la console JNDI View, qui sera utilisé dans la suite de ce chapitre.

Global JNDI Namespace ­ contexte accessible en dehors de la machine virtuelle du serveur. Les ressources qui sont liées à ce contexte, peuvent être utilisées par des applications ne partageant pas la même machine virtuelle que JBoss, un client lourd par exemple. Cela peut être, par exemple, les ressources de type message (queue et topic), ou les références aux EJB déployées, qui doivent pouvoir être utilisées en dehors de JBoss.

java: ­ contexte accessible uniquement au sein de la machine virtuelle du serveur, où se retrouvent les ressources de type sources de données, services de sécurité JAAS. Ce sont des services qui seront liés à des contextes d’application Web ou EJB déployés sur le serveur.

java:comp ­ contexte JNDI utilisable au sein d’une application Web ou EJB. Lorsqu’un EJB ou une application Web est déployée par JBoss, un contexte de nommage propre est créé, accessible uniquement dans l’application. Au sein de ce contexte, des liens sont créés pour référencer des contextes de plus haut niveau.

b. Console de visualisation JNDI

Vous pouvez retrouver l’ensemble des entrées JNDI sur JBoss par l’appel de la console de visualisation JNDI.

Nous allons décrire comment accéder et utiliser la vue JNDI. Dans la suite de cet ouvrage, lorsque nous nous réfèrerons à la vue JNDI, c’est à cet ensemble d’opérations que nous réfèrerons.

Déployez à l’aide d’Eclipse, le composant EAR fourni dans l’archive téléchargée. Il s’agit du projet Chap 3 ­ EAR. Vérifiez que le serveur JBoss est démarré. Attendez le démarrage complet du serveur.

Dans un navigateur, saisissez l’url : http://localhost:8080/jmx­console.

Vous obtenez la page qui visualise la liste des services JMX chargés. Recherchez le service JNDIView.

Cliquez sur le lien service=JNDIView. Vous obtenez la liste des opérations disponibles pour ce MBean.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

Retrouvez l’opération list() et cliquez sur le bouton Invoke pour appeler l’opération.

Vous avez maintenant l’ensemble des entrées JNDI de JBoss.

Vous devriez retrouver :

le contexte de nommage de l’application Web :

java:comp namespace of the Chap 3 - Client web.war application: +- UserTransaction[link -> UserTransaction] (class: javax.naming.LinkRef) +- env (class: org.jnp.interfaces.NamingContext) | +- service[link -> ejb2/service-calculette/remote] (class: javax.naming. LinkRef) | +- security (class: org.jnp.interfaces.NamingContext) | | +- realmMapping[link -> java:/jaas/other] (class: javax.naming. LinkRef) | | +- subject[link -> java:/jaas/other/subject] (class: javax.naming. LinkRef) | | +- securityMgr[link -> java:/jaas/other] (class: javax.naming. LinkRef) | | +- security-domain[link -> java:/jaas/other] (class: javax.naming. LinkRef)

Puis, les contextes de nommage des deux EJB :

Ejb Module: Chap 3 - EJB.jar java:comp namespace of the Calculette bean: +- env (class: org.jnp.interfaces.NamingContext) java:comp namespace of the ServiceCalculette bean: +- env (class: org.jnp.interfaces.NamingContext) | +- calcul[link -> ejb2/calculette/local] (class: javax.naming.LinkRef) | +- tva (class: java.lang.Double)

Et enfin, en bas de la page HTML, nous trouvons le contexte de nommage global, qui est accessible en dehors du serveur. Les associations entre les EJBs et leur nom JNDI sont effectuées ici.

+- HiLoKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator.hilo. HiLoKeyGeneratorFactory) +- UILConnectionFactory[link -> ConnectionFactory] (class: javax.naming.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

LinkRef) +- ejb2 (class: org.jnp.interfaces.NamingContext) | +- service-calculette (class: org.jnp.interfaces.NamingContext) | | +- remote (proxy: $Proxy62 implements interface fr.editions.eni. jboss.chap3.ejb2.ServiceCalculetteHome,interface javax.ejb.Handle) | +- calculette (class: org.jnp.interfaces.NamingContext) | | +- local (proxy: $Proxy60 implements interface fr.editions.eni. jboss.chap3.ejb2.CalculetteLocalHome) +- QueueConnectionFactory (class: org.jboss.naming.LinkRefPair) +- UUIDKeyGeneratorFactory (class: org.jboss.ejb.plugins.keygenerator. uuid.UUIDKeyGeneratorFactory)

JBoss crée donc :

un contexte de nommage JNDI global, directement utilisable avec InitialContext, qui contient les noms JNDI des EJB, des services de transaction, des services de gestion des messages…

un contexte java: contenant les noms des services d’authentification, de mails…

plusieurs contextes java:comp/env, un par application, EJB, contenant par défaut des liens à des nommages de plus haut niveau mis en place par JBoss, des liens vers d’autres ENC et des entrées d’environnement décrits dans les fichiers de configuration jboss.xml et jboss­web.xml.

Il est important, au sein d’une application Web ou d’un composant EJB, d’utiliser l’ENC local. En effet, le contexte de déploiement des EJB peut être différent d’un serveur à l’autre. Nous allons donc voir comment

lier les nommages globaux aux contextes de nommage locaux des applications. Ceci permet de limiter l’impact du changement aux seuls fichiers de déploiement.

3. Le service de nommage de JBoss : JBossNS

JBossNS est l’implémentation de javax.naming.Context basé, sur une couche socket/RMI. Cette implémentation est optimisée afin, que les appels JNDI effectués dans la même JVM que celle qui exécute le service JBossNS ne passe pas par les sockets.

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

Le service de nommage démarre avec le MBean NamingService qui fournira le service de nommage JNDI et créera le contexte java:comp.

a. Contexte de nommage standard

Les propritétés de ce contexte peuvent être fournies sous forme de fichier jndi.properties, ou associées dans une Hashtable. Elles sont les suivantes :

java.naming.factory.initial : nom complet de la classe qui créera le contexte initial, soit org.jnp.interfaces.NamingContextFactory pour JBoss.

java.naming.provider.url : url du service JNDI auquel le NamingContextFactory doit se connecter, jnp://nom_machine:1099 pour JBoss, où nom_machine est l’ordinateur sur lequel est exécuté le service JNDI.

java.naming.factory.url.pkgs : est la liste des packages permettant de localiser les contextes JNDI jnp: et java:, les packages sont séparés par ":". Pour JBoss il s’agit de org.jboss.naming et org.jnp.interfaces.

java.naming.security.principal : est l’identité de sécurité (nom de l’utilisateur) pour l’authentification auprès du service JNDI, si nécessaire.

java.naming.security.credential : est l’attribut de sécurité (mot de passe, certificat) pour l’authentification auprès du service JNDI, si nécessaire.

Il faut noter que ces propriétés sont aussi accessibles via des champs statiques de l’interface Context. Par exemple, Context.PROVIDER_URL correspond à java.naming.provider.url.

4. Nommage JNDI des composants déployés sous JBoss

L’ensemble des extraits de code présentés ici, repose sur les quatre projets présents dans l’archive téléchargeable :

Chap 3 ­ EJB contient deux EJB, Calculette et ServiceCalculette. ServiceCalculette utilise Calculette.

Chap 3 ­ client web contient l’interface web (accessible par http://localhost:8080/cwc/) permettant d’interroger l’EJB ServiceCalculette.

Chap 3 ­ EAR est le fichier de déploiement EAR de l’application.

Chap 3 ­ client lourd EJB permet de tester l’EJB ServiceCalculette en dehors de la machine virtuelle de JBoss.

Vous pouvez modifier ces projets pour tester les différents nommages JNDI, et donc les recherches dans le contexte approprié, en fonction des types de déploiement. Cet exemple est basé sur les EJB 2. Nous aurons l’occasion de revenir sur le déploiement des EJB 3 et EJB 2. Il ne s’agit ici que de mettre en avant quelques types de déploiements pour voir leur impact sur le nommage des composants dans JNDI.

a. Nommage JNDI par défaut

Pour ce test de déploiement, le fichier jar contenant les EJB (projet Chap 3 ­ EJB) n’a pas de fichier de déploiement propre au serveur, jboss.xml. Le serveur va donc utiliser des noms JNDI par défaut pour le déploiement des EJB. Chaque serveur a une stratégie de nommage différente. Il faut donc éviter d’utiliser les nommages par défaut, car l’application ne serait pas portable aisément d’un serveur à l’autre.

Notez lors du déploiement des EJB, les noms JNDI utilisés. Vous pouvez retrouver ces noms dans la console où JBoss est exécuté (comme ici) ou par la vue JNDI.

16:42:14,187 INFO [BaseLocalProxyFactory] Bound EJB LocalHome ’Calculette’ to jndi ’local/Calculette@26117676’

16:42:14,234 INFO [ProxyFactory] Bound EJB Home

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

’ServiceCalculette’ to jndi ’ServiceCalculette’

Notez l’utilisation d’un espace de nommage local/ pour les interfaces de création locales. Les noms des interfaces d’exposition des méthodes métiers sont utlisées comme nom JNDI par défaut.

L’application Web utilise une servlet pour invoquer les méthodes de l’EJB ServiceCalculette. Afin de ne pas coder directement dans la servlet le nom JNDI, nous utilisons ici une balise <param-value> dans le web.xml.

… <servlet> <description></description> <display-name>CalculerServlet</display-name> <servlet-name>CalculerServlet</servlet-name> <servlet-class> fr.eni.editions.jboss.chap3.web.servlets.CalculerServlet </servlet-class> <init-param> <description>Nom JNDI de l’ejb</description> <param-name>calculetteJNDI</param-name>

<param-value>ServiceCalculette</param-value>

</init-param> </servlet> …

La servlet utilise cette valeur pour retrouver l’interface Home de l’EJB.

… protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException String nomJndi = this.getInitParameter("calculetteJNDI");

String operation = request.getParameter("operation"); double nb1 = Double.parseDouble(request.getParameter("nb1")); double nb2 = Double.parseDouble(request.getParameter("nb2")); String message = ""; try double resultat = 0; InitialContext context = new InitialContext();

ServiceCalculetteHome home =

(ServiceCalculetteHome)context.lookup(nomJndi);

ServiceCalculette bean = home.create(); resultat = bean.calculer(operation, nb1, nb2); message += resultat; …

L’instruction

String nomJndi = this.getInitParameter("calculetteJNDI");

permet de récupérer le nom JNDI de l’EJB dans le fichier web.xml.

Les instructions

InitialContext context = new InitialContext(); ServiceCalculetteHome home = (ServiceCalculetteHome)context.lookup(nomJndi);

permettent de récupérer l’interface Home de l’EJB pour l’utiliser par la suite.

b. Utilisation de jboss.xml

L’utilisation du fichier de déploiement spécifique permet d’imposer un nommage JNDI. Ceci améliore considérablement la portabilité et la maintenance, car le code n’est plus affecté.

Le fichier jboss.xml, sur lequel nous reviendrons plus en détail dans les chapitres suivants, se positionne dans le répertoire META­INF de l’archive jar de l’EJB, avec le fichier ejb­jar.xml.

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

… <enterprise-beans> <session> <ejb-name>Calculette</ejb-name> <local-jndi-name>ejb2/calculette/local</local-jndi-name> </session> <session> <ejb-name>ServiceCalculette</ejb-name> <jndi-name>ejb2/service-calculette/remote</jndi-name> </session> </enterprise-beans> …

L’EJB Calculette ne possède pas d’accès distant, aussi la balise <local-jndi-name> permet de préciser le nom JNDI de l’interface de création locale.

L’EJB ServiceCalculette possède seulement une interface de création distante, le nom JNDI est positionné par la balise <jndi-name>. Un EJB possédant des interfaces de création locale et distante utilise les deux balises.

Le suivi de ce nouveau déploiement nous montre les noms JNDI utilisés.

17:40:23,265 INFO [BaseLocalProxyFactory] Bound EJB LocalHome ’Calculette’ to jndi ’ejb2/calculette/local’

17:40:23,281 INFO [ProxyFactory] Bound EJB Home ’ServiceCalculette’ to jndi ’ejb2/service-calculette/remote’

Il est ainsi possible d’organiser les noms en fonction du type d’application, du type de composant… afin de mieux respecter les conventions ENC.

Le fichier web.xml de l’application Web est lui aussi modifié :

… <init-param> <description>Nom JNDI de l’ejb</description> <param-name>calculetteJNDI</param-name> <param-value>ejb2/service-calculette/remote</param-value> </init-param> …

Le code de la servlet n’évolue pas.

c. Référencement de composants avec <ejb­ref>

Nous pouvons maintenant choisir les noms JNDI de nos composants, mais l’accès à ces composants par notre application Web est effectuée directement par le nom JNDI global de l’EJB. Ceci n’est pas conforme avec les spécifications ENC qui veulent qu’une application utilise son propre contexte de nommage. Ce n’est pas, non plus, portable d’un serveur à l’autre. Il nous faut donc le contexte de nommage global de l’EJB avec le contexte de l’application Web.

La servlet doit utiliser son contexte ENC java:comp/env. Pour cela, nous allons commencer par référencer dans le fichier web.xml l’EJB qui sera utlllisé.

… <ejb-ref> <ejb-ref-name>service</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home> fr.editions.eni.jboss.chap3.ejb2.ServiceCalculetteHome </home> <remote> fr.editions.eni.jboss.chap3.ejb2.ServiceCalculette </remote> <ejb-link>Chap_3_-_EJB#Calculette</ejb-link> </ejb-ref> …

<ejb-ref>…</ejb-ref> encapsule les balises référençant l’EJB utilisé. Si les interfaces locales sont utilisées, dans le cas où le composant client de l’EJB est dans la même machine virtuelle que l’EJB lui­même, il existe une balise <ejb-local-ref>…</ejb-local-ref>. Dans notre exemple, bien que l’application Web soit dans la même machine virtuelle

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

que l’EJB, nous utilisons les interfaces distantes.

<ejb-ref-name>service</ejb-ref-name>représente le nom JNDI dans le contexte java:comp/env. C’est ce nom qui sera utilisé lors de la recherche de l’EJB par la servlet.

<ejb-ref-type>Session</ejb-ref-type>représente le type d’EJB, Session ou Entity.

<home> … </home> correspond au nom pleinement qualifié de l’interface de création distante (de type EJBHome). Pour une interface de création locale (de type EJBLocalHome), nous utiliserions <local-home>…</local-home>.

<remote>…</remote>correspond au nom pleinement qualifié de l’interface distante d’exposition des méthodes métiers (de type EJBObject), une balise <local>…</local> est utilisée pour l’interface distante (de type EJBLocalObject).

<ejb-link>…</ejb-link>permet de lier le composant en cours de description avec le <ejb-name> du fichier ejb­jar.xml. Si l’EJB n’est pas dans le même fichier archive que le client de l’EJB, alors il faut préfixer le nom de l’EJB par le nom de l’archive jar, avec le caractère dièse (#) comme séparateur.

Avec ces indications, la servlet peut utiliser l’EJB. Elle connaît :

le type de l’EJB ;

les classes à utiliser ;

le nom JNDI de l’EJB dans son ENC.

Il faut maintenant que le conteneur web puisse faire le lien entre l’EJB déclaré dans le fichier web.xml et l’EJB déployé. Le fichier jboss­web.xmlpermet de faire ce lien.

… <jboss-web> <context-root>cwc</context-root> <ejb-ref> <ejb-ref-name>service</ejb-ref-name> <jndi-name>ejb2/service-calculette/remote</jndi-name> </ejb-ref> </jboss-web>

Ce fichier complémentaire permet au conteneur de déployer l’application.

<jboss-web>…</jboss-web> est la racine du document jboss­web.xml.

<context-root>…</context-root>correspond au nom de l’application Web. L’url de l’application sera ici http://localhost:8080/wcw/.

<ejb-ref>…</ejb-ref>contient les balises décrivant la liaison. Il existe aussi un <ejb-local-ref> pour l’utilisation des interfaces locales.

<ejb-ref-name>…</ejb-ref-name>contient le nom JNDI utilisé dans le contexte de l’application Web. Il doit correspondre à la balise du même nom, utilisée dans le fichier web.xml.

<jndi-name>…</jndi-name>contient le nom JNDI de l’EJB dans le serveur JBoss. Dans notre exemple, ce nom JNDI est déclaré dans le fichier jboss.xml de l’EJB (cf. précédemment).

d. Référencement dans le fichier ejb­jar.xml

Le projet Chap 3 ­ EJB contient deux EJB. L’EJB ServiceCalculette qui utilise Calculette. Pour les mêmes raisons de portabilité, de respect des spécifications ENC et de maintenance, le référencement des EJB entre eux doit être privilégié. De même, nous allons pouvoir ajouter des entrées JNDI dans le contexte d’un EJB.

… <enterprise-beans> <session> <ejb-name>Calculette</ejb-name> … </session> <session> <ejb-name>ServiceCalculette</ejb-name>

- 10 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

… <env-entry>

<description>Taux de TVA</description> <env-entry-name>tva</env-entry-name> <env-entry-type>java.lang.Double</env-entry-type> <env-entry-value>19.6</env-entry-value> </env-entry> <ejb-local-ref>

<ejb-ref-name>calcul</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local-home> fr.editions.eni.jboss.chap3.ejb2.CalculetteLocalHome </local-home> <local> fr.editions.eni.jboss.chap3.ejb2.CalculetteLocal </local> <ejb-link>Calculette</ejb-link> </ejb-local-ref> </session> </enterprise-beans> …

- 11 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSSx33KKRzIgLAA==-enidentnumber

Services de déploiement

En standard, le répertoire deploy de la configuration serveur active est le répertoire de déploiement des composants. Il suffit donc de copier dans ce répertoire un fichier qui est conforme aux spécifications propre, au composant déployé pour que le composant soit pris en compte par le serveur.

Si vous utilisez la configuration par défaut, c’est­à­dire lancée par :

run -c default

le répertoire de déploiement sera <répertoire installation JBoss>server/default/deploy.

Comme nous avons pu le voir précédemment, ce répertoire de déploiement peut être modifié, en modifiant le service par la console de gestion des services (http://localhost:8080/jmx­console/) ou dans le fichier conf/jboss­service.xml.

Chaque type de fichier est pris, géré par un service de déploiement adapté. Vous retrouvez tous ces services dans la console JMX.

Les principaux services de déploiement disponibles, en fonction des configurations du serveur, sont les suivants :

service EARDeployer : gère le déploiement des applications d’entreprise, fichiers en .ear. Le fichier de configuration de ce service est deploy/ear­deployer.xml.

service EJBDeployer : gère le déploiement des EJB2, fichiers en .jar. Le fichier de configuration de ce service est deploy/ejb­deployer.xml.

service SARDeployer : gère le déploiement des services JBoss, les MBeans, fichiers en *­service.xml ou en .sar. Le fichier de configuration de ce service est conf/xmdesc/org.jboss.deployement.SARDeployer­xmbean.xml.

service RARDeployer : gère le déploiement des connecteurs JCA, fichiers en .rar. Le fichier de configuration de ce service est deploy/jbossjca­service.xml.

service ConnectionFactoryDeployer : gère le déploiement des sources de données, fichiers en .*­ds.xml. Ce service est un complément du service RARDeployment. Le fichier de configuration de ce service est deploy/jbossjca­service.xml.

service EJB3Deployer : gère le déploiement des connecteurs EJB3, fichiers en .jar. Le fichier de configuration de ce service est deploy/ejb3­interceptors­asp.xml et deploy/ejb3.deployer/META­INF/jboss­service.xml.

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOVe4Ux7/kcyICwA=-enidentnumber

Conteneur Web

Le rôle de conteneur Web de JBoss est assuré par un service basé sur Tomcat, projet du groupe Apache (http://tomcat.apache.org). Jusqu’à la version 4.2 de JBoss, la version de Tomcat était clairement nommée dans le service, il s’agissait du service jbossweb-tomcat-55.sar, qui utilisait la version 5.5 de Tomcat.

À partir de la version 4.2 de JBoss, l’identification de la version de Tomcat est moins aisée, car le service est assuré par jboss-web.deployer. L’analyse des informations de démarrage de JBoss montre que le service est identifié par JBossWeb/2.0.1.GA.

09:08:39,640 INFO [StandardEngine] Starting Servlet Engine: JBossWeb/2.0.1.GA

Le service JMX se retrouve dans la console JMX, sous Catalina. Catalina est le nom du conteneur de Servlets.

JBossWeb repose sur Apache Tomcat. La version utilisée peut être identifiée en analysant les trames HTTP. À une requête sur http://localhost:8080/, le serveur renverra une réponse commençant par l’en­tête suivante :

HTTP/1.x 200 OK Server: Apache-Coyote/1.1 X-Powered-By: Servlet 2.4; JBoss-4.2.2.GA (build: SVNTag=JBoss_4_2_2_GA date=200710221139)/Tomcat-5.5

Connaître la version de Tomcat peut s’avérer important pour savoir avec quelles spécifications de Servlet et JSP nous pouvons travailler.

Une des grandes différences entre les versions 6 et 5.5 de Tomcat est que seule la version 6 supporte l’injection de ressource par annotation.

Avec Tomcat 5.5, vous ne pouvez pas utiliser :

@Resource(name="jdbc/bovoyageDSN") private DataSource dataSource;

Mais vous devez alors effectuer une recherche JNDI :

String nomSource = "java:comp/env/jdbc/bovoyageDSN"; try Context contexteJndi = new InitialContext(); DataSource dataSource = (DataSource) contexteJndi.lookup(nomSource); …

Par défaut, le port d’écoute HTTP est le port 8080.

1. Fichier de configuration jboss­service.xml

Version Apache Tomcat Spécification Servlet Spécification JSP

6.0.14 2.5 2.1

5.5.25 2.4 2.0

4.1.36 2.3 1.2

3.3.2 2.2 1.1

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzleK6BqORzIgLAA==-enidentnumber

Le fichier de configuration du service Tomcat est jboss­service.xml qui se trouve dans le répertoire <repertoire_serveur>/deploy/jboss­web.deployer/META­INF, où <repertoire_serveur> est le répertoire où se trouve le serveur actif.

Le fichier jboss­service.xml contient la définition des alias pour les hôtes virtuels, la liaison vers le domaine de sécurité et permet le contrôle de Tomcat.

Les attributs de configuration du MBean sont positionnés dans des éléments XML <attribute> de la forme <attribute name="nom_attribut">valeur attribut</attribute>.

Les principaux attributs sont les suivants :

DefaultSecurityDomain : domaine de sécurité JAAS (Java Authentification and Authorization Service) utilisé en l’absence d’une configuration explicite dans le fichier jboss­web.xml du fichier WAR.

UseJBossWebClassLoader : indique si Tomcat doit utiliser le chargeur de classes de JBoss, par défaut positionné à vrai (true). Ceci implique que les classes dans des répertoires WEB­INF/classes et WEB­INF/lib seront incorporées dans l’espace de stockage commun des classes chargées, cela permet aux classes et aux ressources d’être partagées entre les applications Web.

LenientEjbLink : positionné à vrai par défaut, les erreurs sur ejb-link seront ignorées pour favoriser la prise en compte du jndi-name du fichier jboss­web.xml.

SnapshotMode : utilisé en mode cluster, admet les valeurs instant ou interval. Dans le mode instant, les changements dans les sessions sont propagés aux autres conteneurs du cluster. Dans le mode interval, cette mise à jour est effectuée à intervalles réguliers, cet intervalle est précisé par l’attribut SnapshotInterval.

SnapshotInterval : intervalle de mise à jour des sessions des conteneurs d’un cluster en mode interval. Cet intervalle est exprimé en millisecondes, par défaut sa valeur est 1 000 ms, soit 1 seconde.

2. Fichier de configuration server.xml

Le fichier de configuration du serveur Tomcat lui­même est server.xml qui se trouve sous <repertoire_serveur>/deploy/jboss­web.deployer.

Ce fichier contrôle le fonctionnement de Tomcat. Ainsi, c’est dans ce fichier que sont définis les ports d’écoute de Tomcat.

La directive <Connector port="8080"…> permet l’écoute sur le port 8080. C’est ici que nous définirons le port d’écoute pour la mise en place du protocole HTTPS.

La racine du fichier est l’élément <Server> dans lequel il est possible de retrouver des éléments de déclaration des classes listeners et un élément <Service>.

L’élément <Service> contient d’autres éléments dont :

Elément <Connector> : un connecteur permet de configurer une couche de transport qui permet au client d’envoyer les requêtes et de recevoir les réponses.

Elément <Host> : représente une configuration d’hôte virtuel. C’est un conteneur d’applications Web pour un nom DNS particulier.

3. Fichier de configuration web.xml

Le fichier de configuration par défaut des sites web déployés est web.xml situé dans le répertoire <repertoire_serveur>/deploy/jboss­web.deployer/conf. Ce fichier permet la déclaration des servlets propres à Tomcat, et la déclaration des types MIME. Chaque application Web possède son propre fichier web.xml.

4. Fichier de configuration context.xml

Ce fichier de configuration contient le comportement par défaut, pour l’élément Context.

<Context cookies="true" crossContext="true">

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzleK6BqORzIgLAA==-enidentnumber

Il permet de mettre en place le suivi des sessions par les cookies et l’accès au contexte d’application entre les applications web par la méthode ServletContext.getContext(String path).

5. Gestion des ressources statiques

JBoss utilise une application par défaut qui contient les ressources pour l’application racine (root). Cette application par défaut est l’application ROOT.war située sous le répertoire <repertoire_serveur>/deploy/jboss­web.deployer. Vous pouvez y ajouter vos propres ressources statiques qui peuvent être ainsi partagées par plusieurs applications Web. Vous pouvez ainsi créer un répertoire images et y placer des images. L’accès aux images se fera alors par l’URL http://localhost:8080/images/nom_image.jpg.

Le déploiement des applications Web sera détaillé au chapitre Déploiement d’application Web, et la sécurisation des applications Web le sera au chapitre Gestion de la sécurité.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzleK6BqORzIgLAA==-enidentnumber

Service de messagerie JMS

Dans les architectures distribuées hétérogènes, composées d’applications différentes implémentées dans des langages pouvant être différents, la communication entre applications peut être un problème. Parmi les nombreuses solutions à cette problématique, deux grands axes se dessinent.

L’invocation de procédures distantes (RPC ­ Remote Procedure Call), qui peut être effectuée avec des standards tels que RMI pour Java, DCOM pour Microsoft, ou CORBA. Le principe reste le même : invoquer un service sur une machine distante et traiter la réponse.

L’échange de messages entre applications. Ces messages sont véhiculés par une infrastructure particulière, appelée MOM(Middleware Oriented Messaging). Ces messages sont beaucoup plus génériques que l’invocation de services distants et peuvent représentés tout type de contenu, objet sérialisé ou texte.

L’invocation de procédures à distance impose un couplage fort entre les applications car les deux applications doivent être actives en même temps, pour pouvoir communiquer. Les MOM permettent de casser ce couplage car les applications ne communiquent plus directement entre elles.

Il existe de nombreuses solutions, commerciales ou non, de service MOM : IBM.

MQSeries, BEA MessageQ, Microsoft MSMQ, …

L’API JMS permet de spécifier un standard pour l’utilisation des brokers MOM. Il existe deux modes de fonctionnement :

le mode point à point (point to point) ;

le mode publication/abonnement (publish/subscribe).

L’application qui envoie un message est un producteur, celle qui le récupère est un producteur.

1. Mode point à point

Les messages sont échangés par l’intermédiaire d’une file d’attente (message queuing). L’application émettrice du message dépose celui­ci dans la file. Ce message reste dans la file, jusqu’à ce que l’application destinataire le récupère. Le producteur ne spécifie pas l’application cible, mais le nom de la file d’attente. Plusieurs producteurs peuvent mettre des messages dans la file, par contre un message n’est récupéré que par un seul consommateur. Entre le moment où le message est déposé et celui où il est consommé, le broker en assure la persistance.

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWNmu2D/kcyICwA=-enidentnumber

Ce type de middleware est caractérisé principalement par :

la persistance des messages ­ les messages sont stockés jusqu’à leur consommation ;

la qualité de service ­ garantie de livraison de message, gestion de priorités, durée de vie des messages.

2. Mode publication/abonnement

C’est un mode de communication point à multipoints. Chaque message est associé à un sujet (topic). Le consommateur qui veut recevoir un message va s’abonner (subscribe) à un sujet. Le MOM envoie une copie du message à tous les abonnés. Le producteur du message ne connaît pas les consommateurs. C’est le MOM qui gère la liste des abonnés.

Ce type de middleware est caractérisé principalement par :

la rapidité d’échange des messages ­ utilisation de protocoles dédiés comme IP multicast.

3. Spécification JMS

Comme pour JDBC vis­à­vis des bases de données, l’API JMS permet de normaliser l’accès aux différents MOM.

Les interfaces principales de cette API sont :

QueueConnection ou TopicConnection : interface de connexion à la structure MOM, la classe d’implémentation est créée via un Factory ;

QueueSession ou TopicSession : session qui permet de créer les producteurs (QueueSender ou

- 2 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWNmu2D/kcyICwA=-enidentnumber

TopicPublisher) et les consommateurs (QueueReceiver ou TopicSubscripter) de messages ;

Queue : la file de messages ;

Topic : un sujet.

4. Modèle de programmation JMS

Le modèle de programmation JMS est toujours sensiblement le même.

1. Localisation du driver JMS. Ce driver est accessible auprès de JNDI sous le nom "ConnectionFactory".

2. Création d’une connexion JMS par l’intermédiaire d’un factory. Pour une connexion point à point, il faudra utiliser une QueueConnectionFactory, et pour une connexion publication/abonnement un TopicConnectionFactory.

3. Obtention de la session JMS auprès de la connexion. Il s’agira d’une QueueSession pour du point à point, ou d’un TopicSession pour du publication/abonnement.

4. Recherche auprès du service JNDI de la destination JMS, par exemple "queue/testQueue" ou "topic/testTopic".

5. Création du producteur ou du consommateur. Le producteur enverra le message. Le consommateur sera averti de l’arrivée d’un message via l’implémentation d’un javax.jms.MessageListener.

6. Phase d’émission ou de réception du message.

5. Les EJB orientés message

Dans l’architecture JEE, les EJB MD (Message Driven), MED pour Message Driven Bean, sont des composants permettant la réception des messages JMS qu’ils soient point à point ou de type publication/abonnement.

L’utilisation et le déploiement des EJB MD seront détaillés dans le chapitre Déploiement des EJB.

- 3 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWNmu2D/kcyICwA=-enidentnumber

Service de transaction JTA

Plusieurs tâches unitaires peuvent concourir à l’obtention d’un résultat global. L’ensemble des tâches unitaires sont regroupées au sein d’une transaction.

Ainsi, cela peut être :

plusieurs écrans de saisies d’informations pour l’enregistrement d’un nouveau client dans une application d’entreprise ;

ou, un ensemble de tâches permettant le débit d’un compte client pour créditer un compte fournisseur.

Le résultat de ces tâches unitaires doit être cohérent pour valider l’écriture en base de données. Si toutes les tâches unitaires se sont correctement déroulées, l’opération globale sera validée (le commit), sinon, les données utilisées par les tâches unitaires doivent revenir dans leur état initial (le rollback).

Les contraintes que doit vérifier une transaction sont résumées par l’acronyme ACID :

Atomicité : la transaction effectue une mise à jour, ou aucune ;

Cohérence : la cohérence des données doit toujours être assurée, que la transaction soit réussie ou non ;

Isolation : les résultats d’une transaction ne sont visibles qu’une fois que cette transaction est validée ;

Durabilité : une fois la transaction validée le système est dans un état stable durable.

La gestion des transactions est facilitée au moyen d’un ensemble d’interfaces standardisées au sein de JTA (Java Transaction API). Le gestionnaire de transaction défini dans JTA repose sur une interface de base : javax.transaction.TransactionManager. JBoss met à disposition un gestionnaire de transaction au travers d’un nommage JNDI : java:/TransactionManager.

Les transactions peuvent devenir complexes en fonction du nombre de ressources et de serveurs impliqués :

transaction locale dans laquelle sont impliqués une ressource et un serveur ;

transaction distribuée sur un seul serveur mais impliquant plusieurs ressources ;

transaction distribuée impliquant plusieurs serveurs et une seule ressource ;

transaction distribuée impliquant plusieurs serveurs et plusieurs ressources.

La gestion des transactions sera détaillée dans le chapitre Gestion des transactions.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzoSiGLKORzIgLAA==-enidentnumber

Service de connecteurs JCA

L’intégration d’application d’entreprise (EAI ­ Enterprise Application Integration) est une architecture qui permet à des applications hétérogènes de gérer les échanges inter­applications.

Les échanges entre applications requièrent les éléments suivants :

des connecteurs qui jouent le rôle d’interface entre l’architecture EAI et les applications. Ils sont à l’écoute des évènements des applications et transmettent et/ou transfèrent des données depuis/vers les applications ;

des couches d’associations (mapping) permettant l’échange des données, objets métiers, entre les différentes applications, via les connecteurs ;

une couche de transport qui permet l’acheminement des données entre les applications.

La spécification JCA (Java Connector Architecture), à l’instar de JDBC par exemple, au développeur de bénéficier d’une API standard quel que soit le fournisseur de l’application.

Les connecteurs peuvent être vus comme des contrats système entre des applications d’entreprise et le serveur d’applications.

L’objectif est donc d’assurer une collaboration entre ces éléments hétérogènes pour que les mécanismes de transaction, de sécurité et de gestion des connexions soient transparent vis­à­vis des applications hébergées sur le serveur.

Le framework JBossCx implémente l’architecture nécessaire pour l’utilisation de JCA.

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOd+abYL/kcyICwA=-enidentnumber

Service de sécurité JAAS

JAAS (prononcez "Jazz"), pour Java Authentification and Autorisation Service, est un framework de sécurité qui a pour but d’authentifier et gérer les autorisations d’un utilisateur. Il faut prendre ici l’utilisateur au sens large : humain, machine, objet.

Des modules d’authentification peuvent être chaînés les uns aux autres, utilisation d’un lecteur de cartes à puce couplé à une demande de mot de passe, par exemple. L’utilisateur qui passe les modules acquiert une identité virtuelle à laquelle seront associés des droits.

La gestion de la sécurité sera détaillée au chapitre Gestion de la sécurité.

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOcGtAJT/kcyICwA=-enidentnumber

Autres services utiles

1. Service de suivi des threads et de la mémoire

Ce service, géré par le MBeanjboss.system:type=ServerInfo permet de suivre l’usage de la mémoire et des threads de l’instance de JBoss.

Vérifiez dans la console JMX les attributs du MBean et les opérations disponibles.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

Cliquez sur les boutons correspondant aux opérations suivantes pour visualiser l’occupation mémoire de votre machine, ainsi que l’état des threads.

listMemoryPools : affiche la taille et l’utilisation des pools de mémoire dans la JVM ;

listThreadDump : affiche les threads ;

listThreadCpuUtilization : affiche le temps CPU de chaque thread.

2. Service Mail

Le MBean enregistré sous le nom jboss:service=Mail permet d’utiliser des sessions JavaMail au sein de JBoss.

Le fichier de configuration du service est situé sous deploy/mail­service.xml.

<server> <mbean code="org.jboss.mail.MailService" name="jboss:service=Mail"> <attribute name="JNDIName">java:/Mail</attribute>

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

<attribute name="User">nobody</attribute> <attribute name="Password">password</attribute> <attribute name="Configuration"> <!-- A test configuration --> <configuration> <!-- Change to your mail server protocol --> <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="nobody"/> <!-- Change to the mail server --> <property name="mail.pop3.host" value="pop3.nosuchhost.nosuchdomain.com"/> <!-- Change to the SMTP gateway server --> <property name="mail.smtp.host" value="smtp.nosuchhost.nosuchdomain.com"/> <!-- The mail server port --> <property name="mail.smtp.port" value="25"/> <!-- Change to the address mail will be from --> <property name="mail.from" value="[email protected]"/> <!-- Enable debugging output from the javamail classes --> <property name="mail.debug" value="false"/> </configuration> </attribute> <depends>jboss:service=Naming</depends> </mbean> </server>

Ce fichier doit être configuré avec vos paramètres pour pouvoir utiliserJavaMail correctement et pouvoir se connecter aux serveurs SMTP et POP. Les noms des propriétés sont assez explicites. Vous y retrouvez de manière classique toute la configuration nécessaire pour aller déposer des e­mails et les récupérer.

Par exemple :

<property name="mail.pop3.host" value="pop3.nosuchhost.nosuchdomain.com"/>

Cette propriété vous permet de préciser, dans l’attribut value, le serveur POP. Pour le provider Orange cette valeur pourrait être : pop.orange.fr. Ce qui donnerait :

<property name="mail.pop3.host" value="pop.orange.fr"/>

L’API JavaMail étant une API d’assez bas niveau, très proche du protocole, il peut être intéressant d’utiliser des bibliothèques tierces, comme la librairie commons-email du groupe Apache que vous pouvez télécharger à l’adresse http://commons.apache.org. Le codage d’un envoi de mail devient alors très simple.

... SimpleEmail mail = new SimpleEmail();

mail.setMailSessionFromJNDI("java:/Mail"); mail.addTo("adresse.correspondant@provider"); mail.setSubject("TEST MailService"); mail.setMsg("Bonjour - cela fonctionne"); mail.send(); ...

3. Service Log4j

Le MBean Log4jService permet la configuration du système de journalisation Log4j (projet du groupe Apache). Log4j

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

est utilisé par JBoss comme système de journalisation.

Les attributs suivants peuvent être configurés :

ConfigurationURL : URL du fichier de configuration de Log4j, par défaut resource:log4j.xml qui correspond au fichier conf/log4j.xml ;

RefreshPeriod : délai de prise en compte des changements de configuration de Log4j, 60 secondes par défaut ;

CatchSystemErr : booléen indiquant si le flux System.err doit être redirigé vers la catégorie Log4j STDERR, par défaut, mis à vrai ;

CatchSystemOut : booléen indiquant si le flux System.out doit être redirigé vers la catégorie Log4j STDOUT, par défaut, mis à vrai ;

Log4jQuietMode : doit être mis à vrai, se reporter au bug Log4j numéro 696819.

Vous pouvez configurer le service pour avoir plus ou moins d’informations. Par exemple, lors du débogage des EJB CMP, il peut être intéressant de suivre l’envoi des requêtes vers la base de données. Vous pouvez ajouter l’appender suivant :

<appender name="CMP" class="org.apache.log4j.ConsoleAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="Target" value="System.out"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%dABSOLUTE %-5p [%c1] %m%n"/> </layout> </appender>

4. TimerService

La spécification JMX définie un MBean timer qui peut envoyer des événements temporels à d’autres objets. JBoss fournit un service de configuration du MBean timer . Ce TimerService permet d’aider à la configuration des propriétés suivantes :

NotificationType : type de la notification qui doit être générée. En effet, un Timer peut être configuré par plusieurs TimerService, le type de notification pourra alors servir de filtre ;

NotificationMessage : message pouvant être associé à la notification ;

TimerPeriod : période entre les notifications, exprimée en millisecondes, secondes, minutes et heures ;

Repeatitions : nombre de notifications que le timer doit effectuer, par défaut, zéro pour l’infini ;

TimerBean : nom du timer que le service TimerService doit configurer.

Il existe aussi un écouteur qui peut être utilisé pour journaliser des messages sur un timer, il s’agit de la classeorg.jboss.monitor.services.NotificationListener.

Nous allons créer un service de timer. Pour ce faire, nous allons d’abord créer un fichier timer­service.xml qui comprend les lignes suivantes :

<server> <mbean code="javax.management.timer.Timer" name="jboss.monitor:name=Chronos,type=Timer" /> </server>

Vérifiez que JBoss est bien démarré, et copiez ce fichier dans le répertoire deploy de votre configuration serveur.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

Vérifiez qu’il n’y a pas d’erreur dans la console.

Ouvrez la console d’affichage des nommages JNDI. Vous devez trouver le service déployé.

Vous pouvez cliquer sur le lien pour voir les attributs et les opérations disponibles sur le MBean.

Voici une nouvelle manière de déployer un MBean, par un fichier XML contenant une configuration serveur.

Nous allons maintenant configurer notre MBean avec un service TimerService. Pour cela, nous allons ajouter dans notre fichier timer­service.xml les lignes suivantes :

<mbean code="org.jboss.monitor.services.TimerService" name="jboss.monitor:name=Chronos,type=TimerService"> <attribute name="NotificationType"> jboss.monitor. Chronos </attribute> <attribute name="NotificationMessage"> Bonjour tout le monde !!! </attribute> <attribute name="TimerPeriod">10sec</attribute> <depends optional-attribute-name="TimerMBean"> jboss.monitor:name=Chronos,type=Timer </depends> </mbean>

Nous configurons ici l’envoi d’un message "Bonjour tout le monde !!!" toutes les 10 secondes.

Pour que notre message soit affiché, nous allons mettre en place un écouteur.

Toujours dans le fichier timer­service.xml, ajoutons les lignes suivantes :

<mbean code="org.jboss.monitor.services.NotificationListener" name="jboss.monitor:service=NotificationListener"> <attribute name="SubscriptionList"> <subscription-list> <mbean name="jboss.monitor:name=Chronos,type=Timer" /> </subscription-list> </attribute> </mbean>

Après chaque modification du fichier, supprimez celui qui est dans le répertoire deploy pour y mettre la nouvelle version à la place.

Si vous regardez la console JNDI, vous devez avoir les trois services déployés :

Dans la console de JBoss, vous devez avoir l’affichage des messages :

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

17:35:05,484 INFO [NotificationListener] Got notification (#1): javax.management.timer.TimerNotification[source=jboss.monitor:name= Chronos,type=Timer][type=jboss.monitor.chronos][message=Bonjour tout le monde !!!], handback: null

Le fichier complet timer­service.xml est le suivant :

<server> <mbean code="javax.management.timer.Timer" name="jboss.monitor:name=Chronos,type=Timer" /> <mbean code="org.jboss.monitor.services.TimerService" name="jboss.monitor:name=Chronos,type=TimerService"> <attribute name="NotificationType">jboss.monitor.chronos</attribute> <attribute name="NotificationMessage">Bonjour tout le monde !!!</attribute> <attribute name="TimerPeriod">10sec</attribute> <depends optional-attribute-name="TimerMBean"> jboss.monitor:name=Chronos,type=Timer </depends> </mbean> <mbean code="org.jboss.monitor.services.NotificationListener" name="jboss.monitor:service=NotificationListener"> <attribute name="SubscriptionList"> <subscription-list> <mbean name="jboss.monitor:name=Chronos,type=Timer" /> </subscription-list> </attribute> </mbean> </server>

Tout MBean peut implémenter un écouteur pour être invoqué par le TimerService.

5. Service de planification

À la différence du TimerService, leSchedulerService peut directement invoquer des méthodes callback sur des instances de classes quelconques, ou une opération d’un MBean.

Les attributs suivants sont utilisés pour configurer le MBean :

InitialStartDate : date de départ de la planification. Peut être NOW qui correspond à la date courante plus une seconde, la valeur en millisecondes depuis le 01/01/1970 ou une chaîne du type "M/d/yy h:m a". Si la date est antérieure à la date de démarrage du serveur, une nouvelle date est calculée, qui respecte le délai entre les répétitions, défini dans SchedulePeriod ;

InitialRepetition : nombre de fois où le service invoquera la méthode callback, ­1 si l’infini ;

StartAtStartup : booléen indiquant si la planification démarre avec le service ;

SchedulePeriod : délai en millisecondes entre deux appels de la méthode callback ;

SchedulableClass : nom complet de la classe qui implémente l’interface org.jboss.varia.scheduler.Schedulable ;

SchedulableArguments : liste des arguments, séparés par une virgule, pour le constructeur de la classe d’implémentation ;

SchedulableArgumentTypes : liste des types des arguments, séparés par une virgule ;

SchedulableMBean : nom complet du MBean cible, qui peut être invoqué ;

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

SchedulableMBeanMethod : nom de la méthode invoquée, sur le MBean cible.

Voici une classe de test triviale qui implémente l’interface org.jboss.varia.scheduler.Schedulable. Elle se trouve dans la librairie scheduler­plugin.jar. Seule une méthode perform(Date,long) est présente dans cette interface. Cette méthode reçoit en paramètres, la date de l’appel et le délai de répétition.

package fr.editions.eni.jboss.service;

import java.util.Date;

import org.jboss.varia.scheduler.Schedulable;

public class TestSchedulable implements Schedulable

private String nom;

private long numero;

public TestSchedulable(String nom, long numero)

this.nom = nom;

this.numero = numero;

@Override public void perform(Date arg0, long arg1)

System.out.println(">>> Bonjour de la part de "+nom);

Cette classe a été mise dans une archive jar, qui a été copiée dans le répertoire lib de la configuration serveur.

Voici le fichier test­scheduler­service.xml qui permettra de lancer le service :

<server> <mbean code="org.jboss.varia.scheduler.Scheduler" name="jboss.eni:service=Scheduler"> <attribute name="StartAtStartup">true</attribute> <attribute name="SchedulableClass"> fr.editions.eni.jboss.service.TestSchedulable </attribute> <attribute name="SchedulableArguments"> TEST,123456789 </attribute> <attribute name="SchedulableArgumentTypes"> java.lang.String,long </attribute> <attribute name="InitialStartDate">NOW</attribute> <attribute name="SchedulePeriod">60000</attribute> <attribute name="InitialRepetitions">-1</attribute> </mbean> </server>

Les attributs correspondant au passage des paramètres pour le constructeur de la classe de test s’y retrouvent.

<attribute name="SchedulableArguments"> TEST,123456789 </attribute> <attribute name="SchedulableArgumentTypes"> java.lang.String,long </attribute>

Le fichier test­scheduler­service.xml doit être placé dans le répertoire deploy. Il ne doit pas y avoir d’erreur dans la console du serveur. Si le déploiement a été correctement effectué, les messages doivent s’afficher dans la console, toutes les 60 secondes.

19:34:15,328 INFO [STDOUT] >>> Bonjour de la part de TEST 19:35:15,328 INFO [STDOUT] >>> Bonjour de la part de TEST

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

19:36:15,328 INFO [STDOUT] >>> Bonjour de la part de TEST

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzdyJTjqORzIgLAA==-enidentnumber

Fichiers de configuration du serveur

Les fichiers de configuration suivants sont relatifs à la configuration de serveur par défaut, <répertoire installation JBoss>/server/default.

conf/jbossjta­properties.xml : fichiers de configuration des propriétés du service de transaction ;

conf/jboss­log4j.xml : fichier de configuration pour le service de journalisation Log4j ;

conf/jboss­minimal.xml : exemple d’un fichier de configuration minimal, cette configuration correspond à celle utilisée dans le fichier jboss­service.xml dans la configuration serveur minimal ;

conf/jboss­service.xml : décrit les services de base de la configuration serveur default ;

conf/jndi.properties : paramètres par défaut du contexte JNDI InitialContext lorsque celui­ci est instancié sans paramètres ;

conf/login­config.xml : fichier de configuration pour les authentifications ;

conf/standardjboss.xml : configuration par défaut du conteneur d’EJB 2 ;

conf/standardjbosscmp­jdbc.xml : configuration par défaut pour le mapping objet ­ relationnel, des EJB 2 entités de type CMP ;

deploy/jboss­web.deployer/server.xml : configuration par défaut du conteneur Web (Tomcat).

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzN0mop6ORzIgLAA==-enidentnumber

Persistance des objets

Dans l’architecture en couches des applications distribuées, la couche de persistance permet de faire le lien avec la sauvegarde physique des données.

Nous avons des objets qui représentent des entités avec des états :

une personne avec son nom, son âge, sa date de naissance... ;

un compte bancaire avec son numéro, son solde... ;

une adresse ;

etc.

Ces objets ont des états en mémoire et si l’application est arrêtée, cet état n’est pas sauvegardé. Le développeur doit donc se soucier du maintien de l’état dans une base de données : la persistance. Le nec plus ultra pour le développeur serait qu’il n’ait pas à se soucier des mécanismes sous­jacents qui permettent aux objets d’être "persistés". Ceci arrivera peut­être un jour...

Cette sauvegarde est principalement effectuée dans une base de données, en général, relationnelle : Oracle, MySql, PosgreSQL, HSQLDB. Elle peut aussi être effectuée par sérialisation utilisation de fichiers, de XML, etc.

Cette couche va encapsuler les mécanismes qui auront la responsabilité de sauvegarder, modifier ou restaurer l’état de certains des objets métiers de l’application.

Il ne doit pas y avoir de requêtes SQL, ou autres, qui parsèment l’ensemble du code des autres couches applicatives. Idéalement, le développeur ne devrait pas avoir à se soucier des mécanismes sous­jacents utilisés pour permettre la persistance de ses objets. Cette couche doit aussi permettre de garder une certaine indépendance vis­à­vis de l’architecture de la base de données utilisée. Les couches de persistance s’appuient sur l’API JDBC.

De très nombreuses implémentations de cette couche peuvent être envisagées :

codage de la couche par le développeur en utilisant l’API JDBC ;

utilisation du framework Hibernate qui est inclus dans la distribution JBoss ;

utilisation des EJB entités ;

utilisation d’autres frameworks...

1. Codage du pattern DAO

Si le codage de la couche est effectué par le développeur, celui­ci mettra certainement en pratique le design pattern DAO (Data Acces Object). Ce design pattern de conception montre comment encapsuler tous les accès aux sources de données. En général, à chaque objet métier est associé un objet DAO qui contient le code SQL nécessaire à la sauvegarde, modification, suppression en base de données.

Les sources présentés ici, sont issues du projet "Chap4 ­ JDBC". Par exemple, une classe métier Ville est associée à une classe VilleDAO qui contiendra les méthodes permettant l’ajout, les recherches en base de données, etc. Les méthodes de cette classe DAO renvoient un objet, ou des collections d’objets, de type Ville. Les classes de type Ville sont des classes en général, très simples, constituées par des méthodes de type setter/getter, appelées aussi POJO (Plain Old Java Object).

Le codage de ces classes DAO n’est pas complexe, mais s’avère très vite fastidieux, voire ennuyeux car répétitif. En général, des méthodes nommées getQuelqueChoseByAutreChose(…) encapsulent des requêtes SQL du type "SELECT quelqueChose FROM table WHERE colonne=autreChose;".

La classe Ville :

public class Ville implements Serializable

private String codePostal;

private String nom;

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzEcY1wqORzIgLAA==-enidentnumber

public Ville()

public Ville(String nom, String cp)

this.nom = nom;

this.codePostal = cp;

public String getCodePostal()

return codePostal;

public void setCodePostal(String codePostal)

this.codePostal = codePostal;

public String getNom()

return nom;

public void setNom(String nom)

this.nom = nom;

public String toString()

return this.nom + " - " + this.codePostal;

Voici quelques extraits de la classe VilleDAO :

public class VilleDAO

private DAO dao = null;

public VilleDAO(DAO dao)

this.dao = dao;

public Collection<Ville> getVillesByCodePostal(String cp)

throws ApplicationDAOException

Collection<Ville> villes = new ArrayList<Ville>();

String sql = "SELECT * FROM codes_postaux WHERE cp=?"; Connection con = null;

try

con = dao.getConnection(); PreparedStatement st = con.prepareStatement(sql); st.setString(1, cp); ResultSet rs = st.executeQuery(); while (rs.next())

villes.add(this.construireVille(rs));

catch (SQLException e)

throw new ApplicationDAOException(e);

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzEcY1wqORzIgLAA==-enidentnumber

finally

try

dao.releaseConnection(con); catch (SQLException e)

return villes;

...

Vous remarquerez que cette classe est construite sur une classe DAO, que nous présenterons dans la section suivante.

Remarquez aussi que les exceptions levées sont d’un type propre à l’application ce qui permet de ne pas "polluer" les autres couches de l’application par des Exceptions trop spécifiques qui exposent les APIs utilisées. En effet, la couche DAO gère la persistance, si une exception d’une API de bas niveau remonte vers la couche métier ou la couche de présentation, on supprime le couplage faible qui doit exister entre les couches de l’application. La classe ApplicationDAOException vient encapsuler le type réel de l’exception. Cette classe est très simple et permet de changer la couche DAO, qui peut générer d’autres types d’exceptions, sans impacter les couches utilisatrices.

public class ApplicationDAOException extends Exception

private static final long serialVersionUID = 1L;

public ApplicationDAOException()

public ApplicationDAOException(String cause)

super(cause);

public ApplicationDAOException(Exception e)

super(e);

public ApplicationDAOException(String cause,Exception e)

super(cause,e);

Le modèle général de codage utilisant JDBC est le suivant :

obtention d’une connexion au serveur de base de données ;

création d’une requête SQL ;

exécution de la requête SQL ;

traitement du jeu de résultat ;

fermeture de la connexion.

Le codage des classes DAO est toujours basé sur le même schéma, symbolisé par l’acronyme CRUD (Create, Read, Update and Delete) pour la création, la lecture, la mise à jour et la suppression des enregistrements en base.

2. Source de données et pool de connexion

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzEcY1wqORzIgLAA==-enidentnumber

Si le codage lui­même n’est pas complexe, il se pose malgré tout, très vite, dans les environnements distribués, un problème important : comment gérer les connexions et déconnexions à la base de données. De manière classique, la connexion et la déconnexion sont effectuées via la classe java.sql.DriverManager de JDBC.

connexion = DriverManager.getConnection(url,user,pswd)

C’est donc le développeur qui gère les connexions. Mais, par exemple pour un site web, les cycles de connexion/déconnexion peuvent être très fréquents et le nombre de connexions nécessaires à un instant donné, très important. Ce mode de fonctionnement est donc inadapté.

Pour pallier à ce problème, les serveurs d’applications fournissent un service de source de données qui utilise un mécanisme de pool de connexion. Le développeur n’utilise plus le DriverManager, mais une classe javax.sql.DataSource qui est fournie par le serveur. Attention, cette classe n’est utilisable que dans un environnement qui peut la fournir, elle ne l’est pas dans une application autonome.

Ce n’est plus l’application qui gère les connexions, c’est le serveur. Le serveur maintient un certain nombre de connexions qui sont physiquement connectées à la base de données, et qui sont distribuées au fur et à mesure des besoins de l’application. L’appel de la méthode getConnection() permet de récupérer une connexion dans le pool. Une connexion est remise dans le pool après un appel à la méthode close(), ou après un timeout.

Les sources de données sont montées dans le nommage JNDI par le serveur. Pour utiliser une source de données, il faut donc :

effectuer une recherche JNDI ;

demander une connexion.

Une classe spécifique peut gérer les connexions et déconnexions à la base. C’est ici, la classe appelée DAO qui permet d’encapsuler le type réel utilisé pour l’accès aux objets java.sql.Connection. En effet, une application non distribuée utilisera le DriverManager, tandis qu’une application exécutée au sein d’un conteneur utilisera de préférence une DataSource.

public class DAO

String driver; String url; String user; String pswd; DataSource dataSource=null;

public DAO(String driver, String url,

String user, String pswd) throws ClassNotFoundException

this.driver = driver;

this.url = url;

this.user = user;

this.pswd = pswd;

Class.forName(driver);

public DAO(DataSource dataSource)

this.dataSource = dataSource;

public Connection getConnection() throws SQLException

Connection connexion = null;

if(this.dataSource!=null)

connexion = this.dataSource.getConnection();

else

connexion = DriverManager.getConnection(url,user,pswd);

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzEcY1wqORzIgLAA==-enidentnumber

return connexion;

public void releaseConnection(Connection con)

throws SQLException

con.close();

Les classes du modèle de VilleDAO vont demander à la classe DAO une connexion à la base. La classe DAO, en fonction de son mode de construction, par une DataSource, ou par les paramètres de connexion, renverra la connexion adéquate.

3. Autre modèles de conception de la couche de persistance

Lors du codage des couches DAO, il est très vite visible que le code est très répétitif et pourrait presque être généré automatiquement. L’objectif des solutions présentées ici est donc d’éviter le codage des requêtes SQL avec l’utilisation de JDBC.

D’autres solutions sont envisageables :

la sérialisation des instances ;

utilisation des EJB (EJB 2 ou EJB 3) dont le déploiement est détaillé dans cet ouvrage ;

utilisation de JDO (Java Data Object) qui va enrichir le pseudo­code de la classe à persister ;

utilisation de framework de mapping entre les classes et leur représentation en base, comme Hibernate (abordé plus loin), ou iBatis.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzEcY1wqORzIgLAA==-enidentnumber

Paramétrage des sources de données

JBoss fournit un moyen très simple et très souple de configurer une source de données. La description de la configuration de connexion à la base de données se trouve dans un fichier qui doit être nommé yyy­ds.xml où yyy est le nom (arbitraire) de la configuration de votre source de données et ds pour data source. Ainsi, une configuration pour une base de données MySql pourrait être contacts­ds.xml.

Ce fichier est simplement mis dans un répertoire de déploiement pour être pris en compte par JBoss. La prise en compte de cette configuration est effectuée par le service JCA de JBoss. Le modèle de nommage du fichier est nécessaire pour qu’il soit pris en compte par la classe XSLSubDeployer.

Plusieurs sources de données peuvent être configurées dans un même fichier, mais une bonne habitude est de mettre en place un fichier par source de données, ou par base de données. Le fichier <installation_jboss>/docs/dtd/jboss­ds_1_5.dtd décrit la DTD complète de la structure de ce fichier de configuration. Nous allons décrire ici les principaux éléments.

1. Principaux éléments du fichier de configuration

a. Éléments de base

<datasources> : élément racine du fichier de configuration yyy­ds.xml.

<!ELEMENT datasources (mbean | local-tx-datasource | xa-datasource | no-tx-datasource | ha-local-tx-datasource | ha-xa-datasource)*>

<mbean> : des MBeans peuvent être spécifiés pour qu’ils puissent être configurés avant leur utilisation par la source de données ;

<no-tx-datasource> : utilisé pour des sources de données sans le support de gestion des transactions ;

<local-tx-datasource>, <xa-datasource> : sources de données avec support transactionnel. Lorsqu’une connexion doit être utilisée, ou qu’une transaction démarre, un contrôle de l’existence d’une connexion déjà engagée dans la transaction est effectué, si cette connexion existe, elle est utilisée. <xa-datasource> permet la gestion des transactions distribuées ;

<ha-local-tx-datasource>, <ha-xa-datasource> : éléments identiques à <local-tx-datasource> et <xa-datasource> avec ajout d’un module expérimental de gestion de pannes sur un cluster de bases de données. Ce module ne doit donc pas être utilisé sur un serveur en production.

b. Principaux éléments de configuration des sources de données

Les éléments suivants doivent être mis dans un des cinq éléments précédents qui définissent une source de données.

<jndi-name> : nom JNDI dans le contexte avec lequel la source de données sera liée. Le nom JNDI est créé par défaut dans le contexte java: qui n’est pas partagé en dehors de la machine virtuelle du serveur.

<user-java-context> : si cet élément contient false, alors la source de données est liée au contexte global JNDI, donc accessible en dehors du serveur. Si cet élément est absent, il est par défaut positionné à true.

<user-name> : identifiant de connexion à la base de données.

<password> : mot de passe de connexion à la base de données. Cet élément, comme l’élément <user-name>, peut être redéfini par l’application lors de l’invocation de getConnection(…), ou par un contexte sécurité JAAS.

<min-pool-size> : minimum de connexions que doit contenir le pool de connexions.

<max-pool-size> : nombre maximum de connexions dans le pool de connexions.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzjVMt2aORzIgLAA==-enidentnumber

<blocking-timeout-millis> : délai d’attente (en millisecondes) d’une connexion avant le déclenchement d’une exception, par défaut : 500 ms.

<idle-timeout-minutes> : délai maximum (en minutes) avant qu’une connexion inutilisée soit fermée.

<new-connection-sql> : ordre SQL devant être exécuté lorsqu’une connexion est créée.

<exception-sorter-class> : nom complet de la classe implémentant l’interface org.jboss.resource.adapter.jdbc.ExceptionSorter, qui permet l’encapsulation des exceptions de connexion.

Les éléments suivants sont communs aux sources de données locale <no-tx-datasource> et <local-tx-datasource>.

<connection-url> : URL de connexion au driver JDBC, de la forme jdbc:mysql://localhost:3306/contacts.

<driver-class> : nom complet de la classe driver JDBC utilisée, par exemple : com.mysql.jdbc.Driver.

Le répertoire <installation_jboss>/docs/examples/jca contient des fichiers type de configuration vers différentes sources de données.

Exemple de fichier de base pour une connexion vers le serveur MySql :

<datasources> <local-tx-datasource> <jndi-name>mysqlContacts</jndi-name> <driver-class>com.mysql.jdbc.Driver</driver-class> <connection-url>jdbc:mysql:///contacts</connection-url> <user-name>eni</user-name> <password>password</password> <min-pool-size>5</min-pool-size> <max-pool-size>20</max-pool-size> </local-tx-datasource> </datasources>

Attention, bien que le déploiement des sources de données par le ficher yyy­ds.xml fonctionne, il arrive que des changements sur le fichier déployé ne soient pas pris en compte. Redémarrez JBoss, si cela persiste,

c’est certainement dû au fait que votre fichier est mal renseigné. Vérifiez la console du serveur pour y voir d’éventuels problèmes. Vérifiez bien que votre fichier suive le modèle yyy­ds.xml.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzjVMt2aORzIgLAA==-enidentnumber

Hibernate

Hibernate est un framework de persistance d’objets java. C’est un ORM (Object Relationnal Mapping), c’est­à­dire qu’il prend en charge le mapping entre un objet java de type JavaBean et la base de données.

JBoss intègre Hibernate depuis la version 4.0.

Pour éviter les confusions entre les JavaBeans et les EJB, le terme POJO (Plain Old Java Object) est utilisé pour désigner les classes java très simples. Ces classes obéissent aux spécifications de base des JavaBean. Elles

sont sérialisables, possèdent un constructeur par défaut et des getters/setters pour accéder aux propriétés.

Le schéma précédent montre une vue très simplifiée du framework. L’architecture d’Hibernate est très flexible et peut gérer les transactions, les sessions, etc.

Nous allons illustrer ici la manière de configurer Hibernate en tant que service JBoss, nous ne présenterons pas le framework Hibernate et son utilisation. Le lecteur intéressé pourra consulter le site de référence d’Hibernate : http://www.hibernate.org/ où il pourra trouver une documentation très fournie, claire... et en français.

Hibernate utilise une base de données et des propriétés de configuration, pour fournir un service de persistance d’objets.

Les fichiers XML de configuration d’Hibernate (fichiers HBM) vont associer les propriétés des objets avec les champs des tables de la base de données. Nous utiliserons des objets de type POJO qui vont traverser toutes les couches applicatives, ce sont les TO, pour Transfer Object. Le fichier d’association Hibernate assure la liaison entre l’objet et la base de données.

L’exemple présenté dans ce chapitre reprend les projets "Chap 4 ­ Hibernate" et "Chap 4 ­ Web", ces deux projets devant être déployés sous JBoss. Dans cet exemple, nous afficherons une liste de destinations de voyage accessible par un site web, à l’adresse : http://localhost:8080/chap4/.

La classe POJO qui nous sert d’objet de transfert est la suivante :

public class DestinationTO implements Serializable

private long id;

private String pays;

private String description;

public DestinationTO()

public long getId()

return id;

public void setId(long id)

this.id = id;

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

public String getPays()

return pays;

public void setPays(String pays)

this.pays = pays;

public String getDescription()

return description;

public void setDescription(String description)

this.description = description;

public String toString()

return this.pays+" : "+this.description;

Cette classe possède trois propriétés :

id : est le reflet de l’identifiant unique en base de données. Cet identifiant est indispensable pour que Hibernate puisse mapper l’objet avec l’enregistrement en base de données, et le synchroniser, si nécessaire.

pays : correspond au pays de la destination.

description : correspond à une description de la destination.

Pour chacune de ces propriétés, nous avons une méthode getter et une méthode setter.

Le fichier XML qui associe la classe aux champs en base de données est le fichier Destination.hbm.xml :

<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="fr.editions.eni.chap4.TO"> <class name="DestinationTO" table="destinations" > <id name="id" column="kp_destination"> <generator class="native" /> </id> <property name="pays" /> <property name="description"/> </class> </hibernate-mapping>

Nous retrouvons ici les éléments suivants :

<hibernate-mapping> : définit les associations entre les propriétés des classes et la base de données. L’attribut package définit le package dans lequel se trouvent les classes, décrites dans les associations.

<class> : définit quelle classe est associée à quelle table, au travers des attributs name et table. Le package n’est pas précisé car il a été défini dans l’attribut package de l’élément parent.

<id> : décrit l’association sur l’identifiant unique. Le nom de l’identifiant dans la classe est précisé par l’attribut name, l’attribut column définissant le nom de la colonne dans la table.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

<generator> définit dans l’attribut class, la stratégie de génération de l’identifiant unique en base, qui correspond ici, à une génération par la base de données. Les stratégies peuvent être :

increment : généré par Hibernate (attention au mode cluster) ;

native : généré par la base de données ;

assigned : assigné, car il s’agit d’une clé primaire naturelle.

<property> définit l’association entre une propriété de la classe dans l’attribut name, et une colonne, en base de données, dans l’attribut column. Si l’attribut column n’est pas présent, Hibernate considère que le nom de la colonne est le même que celui de la propriété.

Nous devons ensuite créer le servie Hibernate. Pour cela, nous allons créer une MBean dont le fichier de configuration jboss­service.xml est le suivant :

<mbean code="org.jboss.hibernate.jmx.Hibernate" name="jboss.har:service=Hibernate"> <attribute name="DatasourceName"> java:/jdbc/BovoyageDS </attribute> <attribute name="Dialect"> org.hibernate.dialect.MySQLDialect </attribute> <attribute name="SessionFactoryName"> java:/hibernate/SessionFactory </attribute> <attribute name="ShowSqlEnabled">true</attribute> <attribute name="CacheProviderClass"> org.hibernate.cache.HashtableCacheProvider </attribute> </mbean>

Il comprend les éléments suivants :

<mbean> : spécifie le nom du service et la classe d’implémentation ;

<attribute name="DatasourceName"> : reprend la valeur d’une source de données liée au contexte JNDI, comme nous l’avons vu dans la section Paramétrage des sources de données de ce chapitre ;

<attribute name="Dialect"> : permet de préciser quel type de SQL est utilisé. Ici, nous utilisons le SQL de MySQL ;

<attribute name="SessionFactoryName"> : définit le nom JNDI de la classe de fabrication des sessions Hibernate. Cette classe sera utilisée par notre application pour récupérer une session Hibernate ;

<attribute name="cacheProviderClass"> : définit la classe qui implémente la stratégie de cache, utilisée par JBoss ;

<attribute name="ShowSqlEnabled"> : permet de visualiser le code SQL généré par Hibernate dans la console de JBoss, ou dans les fichiers log.

Le projet "Chap 4 ­ Hibernate" doit être déployé sous forme d’un fichier HAR dans le serveur JBoss. Sauf plugin spécifique, ou définition d’une tâche Ant, Eclipse ne gère pas les archives Hibernate. Une archive HAR est constituée des classes et fichiers de mapping, ainsi que le fichier de configuration du service qui se trouve dans le META­INF de l’archive. Nous allons donc voir comment déployer notre projet.

Une fois le projet importé sous Eclipse, ouvrez­le, puis sélectionnez­le, et effectuez un clic droit dessus. Choisissez l’option Export du menu contextuel. Un assistant d’exportation s’ouvre alors.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

Dans le menu General, choisissez l’item Archive File, puis cliquez sur le bouton Next.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

Ouvrez le projet Chap 4 ­ Hibernate situé dans la partie gauche. Désélectionnez la case à cocher du projet et sélectionnez les cases à cocher situées en face de "META­INF" et "fr" (début du package) dans le répertoire bin.

Sélectionnez le répertoire de déploiement sur le serveur et donnez un nom à votre archive, en prenant garde de bien lui donner l’extension har.

Vérifiez que l’option Create only selected directories est cochée, puis cliquez sur le bouton Finish.

Vous devez suivre dans la console le déploiement du service Hibernate.

13:59:18,390 INFO [SettingsFactory] Default batch fetch size: 1 13:59:18,390 INFO [SettingsFactory] Generate SQL with comments: disabled 13:59:18,390 INFO [SettingsFactory] Order SQL updates by primary key: disabled 13:59:18,390 INFO [SettingsFactory] Order SQL inserts for batching: disabled 13:59:18,390 INFO [SettingsFactory] Query translator: org.hibernate.hql.ast. ASTQueryTranslatorFactory 13:59:18,390 INFO [ASTQueryTranslatorFactory] Using ASTQueryTranslator Factory 13:59:18,390 INFO [SettingsFactory] Query language substitutions: 13:59:18,390 INFO [SettingsFactory] JPA-QL strict compliance: disabled 13:59:18,390 INFO [SettingsFactory] Second-level cache: enabled 13:59:18,390 INFO [SettingsFactory] Query cache: disabled 13:59:18,390 INFO [SettingsFactory] Cache provider: org.hibernate.cache. HashtableCacheProvider 13:59:18,390 INFO [SettingsFactory] Optimize cache for minimal puts: disabled 13:59:18,390 INFO [SettingsFactory] Structured second-level cache entries:

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

disabled 13:59:18,406 INFO [SettingsFactory] Echoing all SQL to stdout 13:59:18,406 INFO [SettingsFactory] Statistics: disabled 13:59:18,406 INFO [SettingsFactory] Deleted entity synthetic identifier rollback: disabled 13:59:18,406 INFO [SettingsFactory] Default entity-mode: pojo 13:59:18,406 INFO [SettingsFactory] Named query checking : enabled 13:59:18,437 INFO [SessionFactoryImpl] building session factory 13:59:18,687 INFO [SessionFactoryObjectFactory] Not binding factory to JNDI, no JNDI name configured 13:59:18,687 INFO [NamingHelper] JNDI InitialContext properties: 13:59:18,687 INFO [Hibernate] SessionFactory successfully built and bound into JNDI [java:/hibernate/SessionFactory]

Dans la vue des noms JNDI déployés sous JBoss, vous devez aussi retrouver le fabricant de session, sous le contexte java:.

+- hibernate (class: org.jnp.interfaces.NamingContext) | +- SessionFactory (class: org.hibernate.impl.SessionFactoryImpl) +- timedCacheFactory (class: javax.naming.Context)

La partie client est constituée d’une application Web "Chap 4 ­ Web" qui affiche les destinations disponibles. La recherche de la session Hibernate est encapsulée dans une classe de localisation ServiceLocator.

public class ServiceLocator

private static final String HIBERNATE_SESSION_FACTORY =

"java:/hibernate/SessionFactory"; public static SessionFactory getHibernateSessionFactory()

SessionFactory sessionFactory = null;

try

Context ctx = new InitialContext();

sessionFactory = (SessionFactory) ctx.lookup(HIBERNATE_SESSION_FACTORY);

catch (Exception e)

e.printStackTrace(); return sessionFactory;

public static Session getHibernateSession()

Session session = null;

session = getHibernateSessionFactory().openSession();

return session;

Le nommage JNDI utilisé ici est le nommage global. Nous aurions pu utiliser un nom local en liant le nommage global au sein des fichiers jboss­web.xml et web.xml.

La servlet qui utilise la session Hibernate, invoque la méthode getHibernateSession() du ServiceLocator.

public class DestinationsServlet extends javax.servlet.http.HttpServlet

implements javax.servlet.Servlet

static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse

response) throws ServletException, IOException

List destinations = new ArrayList();

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

Session hibernate = null;

hibernate = ServiceLocator.getHibernateSession();

Criteria criteria = hibernate.createCriteria(DestinationTO.class);

destinations = criteria.list();

request.setAttribute("destinations", destinations); RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/destinations.jsp");

rd.forward(request, response); protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException

doGet(request,response);

La servlet récupère ensuite la liste des destinations et met cette liste dans un contexte de requête pour que la page destinations.jsp puisse l’afficher. La récupération de la liste des destinations passe par la création d’un objet Criteria, qui équivaudra à un "SELECT * FROM destinations".

Nous avons pu interroger notre base de données sans une seule ligne de code JDBC. Maintenant, nous pourrions ajouter, supprimer ou modifier des destinations sans rien ajouter, en utilisant la session Hibernate.

Pour résumer l’utilisation que nous avons faite d’Hibernate :

1. création de la source de données, fichier ­ds.xml ;

2. création des POJO ;

3. création du fichier de mapping, fichier ­hbm.xml ;

4. création du fichier décrivant le service Hibernate ;

5. déploiement du fichier HAR contenant les éléments précédents ;

6. codage d’un ServiceLocator, qui sera réutilisé... ;

7. création de l’application cliente.

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz59/c/aORzIgLAA==-enidentnumber

Introduction aux applications Web

Avant de démarrer dans les configurations et déploiements d’une application Web, commençons par faire un point sur ce qu’est une application Web et quelle est la place de ce type d’application dans les spécifications J2EE/JEE.

Une application Web est une application qui utilise HTTP (Hyper Text Transport Protocol) comme couche de transport. Cela veut dire que le client d’une telle application est un navigateur et que l’application elle­même est sur le serveur.

Le navigateur devient donc un client universel, présent sur toute plate­forme, non spécialisé dans une application mais sachant interprété un langage de présentation, le HTML (HyperText Markup Language) et ses enrichissements : CSS, JavaScript.

1. Protocole HTTP

Il est important de ne pas perdre de vue comment fonctionne le protocole HTTP pour bien mesurer les éventuels risques de sécurité et failles que pourrait présenter une application.

Le navigateur va se connecter à une URL, qui correspond à une machine ayant une adresse IP sur le réseau Internet, ou un réseau interne.

http://www.editions.eni.fr/ : permet d’atteindre l’application par défaut sur le serveur correspondant au domaine editions.eni.fr. Le serveur écoute par défaut, le port 80.

http://localhost:8080/jmx­console : permet d’atteindre l’application jmx-console sur la machine où est le navigateur. Le serveur écoute sur le port 8080, qui est précisé ici, car il n’est pas le port par défaut.

Il est donc question d’application par défaut lorsqu’uniquement le domaine est précisé. Et au sein d’une application, il sera question de ressource par défaut.

http://localhost:8080/secu : atteint la ressource par défaut de l’application. Cette ressource par défaut est souvent une page appelée index.html, index.jsp, default.html ou default.htm. Elle est aussi appelée page d’accueil et nous verrons comment la configurer.

http://localhost:8080/secu/admin.jsp : atteint la ressource de type page JSP qui s’appelle admin.jsp.

http://localhost:8080/secu/controleur : atteint la ressource de type servlet qui est associée à l’URL controleur. Il aurait pu s’agir aussi de la ressource par défaut du répertoire controleur de l’application secu, si aucune servlet n’était associée à l’URL.

Lors de la demande de ressource, le navigateur émet une requête HTTP (request).

Lors de l’envoi de la ressource, le serveur émet vers le navigateur une réponse HTTP (response). Il est important de voir que les messages HTTP, requête et réponse, sont constitués de :

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzsUqGMaSRzIgLAA==-enidentnumber

un en­tête qui donne des renseignements au destinataire du message sur l’émetteur et la ressource demandée. Cette en­tête est textuelle, donc lisible très facilement.

un corps de message qui peut être textuel si la réponse est du HTML, mais qui peut être binaire s’il s’agit d’une image par exemple.

L’en­tête et le corps sont séparés par une ligne vide.

Certains outils nous permettent de visualiser les en­têtes des messages. Sous Firefox par exemple, vous pouvez utiliser le plug­in "Live HTTP Headers".

Voici l’en­tête de la requête envoyée vers http://localhost:8080/jmx­console.

GET /jmx-console/ HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive

Et voici l’en­tête de la réponse du serveur :

HTTP/1.x 200 OK Server: Apache-Coyote/1.1 X-Powered-By: Servlet 2.4; JBoss-4.2.2.GA (build: SVNTag=JBoss_4_2_2_GA date=200710221139)/Tomcat-5.5 Set-Cookie: JSESSIONID=5F2D27F3BF97925CA22C0FCB521C290B; Path=/ Content-Type: text/html;charset=ISO-8859-1 Transfer-Encoding: chunked Date: Fri, 05 Sep 2008 06:53:43 GMT

Une requête HTTP commence par une méthode HTTP. Dans l’exemple précédent, la requête du navigateur a utilisé la méthode HTTP GET. Attention, lorsque nous parlons de méthode HTTP, il ne s’agit nullement d’une méthode comme nous l’entendons dans le langage Java, mais d’une commande.

Les méthodes HTTP spécifient le type de requête, donc quelle action doit être effectuée par le serveur :

GET : la méthode HTTP la plus utilisée, demande une ressource au serveur et permet aussi d’envoyer un formulaire en mode GET. Le contenu du formulaire est alors ajouté à l’URL : http://localhost/login.jsp?nom=toto&id=1234.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzsUqGMaSRzIgLAA==-enidentnumber

POST : commande utilisée lors de l’envoi d’un formulaire en mode POST. Le contenu du formulaire est envoyé dans le corps du message et peut être au format binaire.

HEAD : demande d’information sur une ressource.

PUT : remplace ou ajoute une ressource au serveur.

DELETE : supprime une ressource du serveur.

TRACE : permet le diagnostic de la connexion en demandant au serveur de renvoyer ce qu’il a reçu.

CONNECT : permet d’utiliser un proxy comme tunnel.

OPTIONS : permet de connaître les options de communication du serveur.

2. Architecture Web pour J2EE/JEE

Sun définit dans les spécifications servlet 2.2 ce qu’est une application Web. C’est une collection de ressources : servlets, pages HTML, classes et autres pouvant être packagées et pouvant s’exécuter sur des conteneurs provenant de fournisseurs multiples.

Les éléments d’une application Web sont :

des servlets ;

des pages JSP ;

des ressources statiques : images, pages HTML, documents PDF... ;

des applets ;

des composants Java Bean ;

des descripteurs de déploiement ;

des classes utilitaires Java.

L’ensemble de toutes ces ressources sont packagées dans une archive de type jar, dont l’extension est war. Cette archive sera placée dans le répertoire de déploiement des conteneurs pour être automatiquement déployée. La structure d’une archive war est la suivante :

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzsUqGMaSRzIgLAA==-enidentnumber

Cette archive est prise en compte sur tous les conteneurs servlet/JSP. Il arrive que lors de la migration de l’archive d’un système d’exploitation à un autre le répertoire WEB­INF passe en minuscule. Ainsi, les fichiers

de déploiement ne sont plus pris en compte par le conteneur. Nous avons eu ce problème avec des transferts d’archive entre Ubuntu et RedHat. Il faut alors ouvrir l’archive et corriger l’erreur.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzsUqGMaSRzIgLAA==-enidentnumber

Configuration du conteneur Web

Le service de déploiement Web est un service JBoss qui est déployé dans le répertoire deploy de votre configuration serveur. Le répertoire jboss­web.deployer contient l’ensemble des librairies et des fichiers de configuration de base.

Le service est visible dans la console JMX, via http://localhost:8080/jmx­console.

Il est identifié par JBossWeb/2.0.1.GA qui est un sous­projet JBoss contenant la version 5.5 de Tomcat :

Le contenu du répertoire jboss­web.deployer est le suivant :

répertoire conf contenant un fichier web.xml de base pour les applications Web. Ce fichier contient, entre autres, la liste des types MIME.

répertoire jsf­libs contenant les librairies pour les composants JSF (Java Server Face).

répertoire META­INF contenant :

le fichier jboss­service.xml qui configure le service WebServer ;

le fichier webserver­mbean.xml qui configure le déploiement des archives war.

le répertoire ROOT.war qui est une archive décompressée, correspondant à la page d’accueil de JBoss : l’application web par défaut, accessible par http://localhost:8080/

des librairies jar nécessaires au fonctionnement du service et la prise en charge des JSTL (Java Standard TagLib).

le fichier server.xml qui configure le service. Ce fichier contient les configurations des connecteurs d’écoute : port 8080, port sécurisé 8443.

le fichier context.xml de base pour chaque application Web. Ce fichier contient la prise en compte ou non de la sérialisation des sessions et un écouteur (listener) JBoss pour le cycle de vie des servlets.

Il peut être important, pour la mise en place de certains projets, de connaître la version de Tomcat utilisée. Voici un tableau récapitulatif des versions de spécification, par rapport aux versions de Tomcat.

Spec. servlet Spec. JSP Version Tomcat

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

Le conteneur de servlets reçoit les requêtes provenant du navigateur. Il décode l’en­tête HTTP pour appeler ensuite la méthode adéquate pour la servlet.

La servlet codée en Java par le développeur hérite de la classe javax.servlet.http.HttpServlet. La servlet doit être compilée et le fichier .class doit être placé dans le répertoire WEB­INF/classes de l’archive de déploiement. Le développeur doit implémenter les méthodes correspondant aux méthodes HTTP, susceptibles d’appeler la servlet. En général, deux méthodes uniquement sont souvent implémentées :

doGet(...) pour la méthode HTTP GET ;

doPost(...) pour la méthode HTTP POST.

Les méthodes de la servlet doXxx(...) où Xxx correspond à une méthode HTTP, ont la même signature :

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException

Dans cette méthode, les paramètres :

request, de type javax.servlet.http.HttpServletRequest, encapsule la requête provenant du navigateur ;

response, de type javax.servlet.http.HttpServletResponse, encapsule la réponse vers le navigateur.

Les méthodes sont susceptibles de lever des exceptions de type javax.servlet.ServletException ou java.io.IOException.

Les implémentations par défaut de ces méthodes renvoient une erreur 405 avec le message du type "La méthode n’est pas supportée".

1. Les servlets

Le conteneur gère le cycle de vie de la servlet. C’est lui qui invoque les méthodes. La servlet est compilée par le développeur et déclarée dans le fichier web.xml.

Les seules méthodes que, normalement, le développeur doit implémenter sont les méthodes en doXxx(...).

2.5 2.1 6.0

2.4 2.0 5.5

2.3 1.2 4.1

2.2 1.1 3.3

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

Vous trouverez dans le projet "Chap 5 ­Web", les exemples qui nous serviront durant ce chapitre. Commençons par un exemple trivial de codage d’une servlet, il s’agit de la classe HelloServlet.

public class HelloServlet extends javax.servlet.http.HttpServlet

implements javax.servlet.Servlet

static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException

String nom = request.getParameter("nom");

String message = this.getInitParameter("message");

PrintWriter out = response.getWriter();

if (nom != null)

message = message + " " + nom;

out.print("<html><head></head><body>");

out.print("<h2>" + message + "<h2>");

out.print("</body></html>");

Cette servlet est déclarée dans le fichier web.xml.

<web-app> ... <servlet> <description></description> <display-name>HelloServlet</display-name> <servlet-name>HelloServlet</servlet-name> <servlet-class> fr.eni.editions.servlets.HelloServlet </servlet-class> <init-param>

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

<description></description> <param-name>message</param-name> <param-value>Bonjour</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> ... <web-app>

Nous trouvons dans ce fichier deux parties importantes :

la déclaration de la servlet dans l’élément <servlet> qui contient les éléments fils :

<description> facultatif, fournit une description de la servlet ;

<display-name> contient la valeur affichée par certains outils de gestion, facultatif ;

<servlet-name> est le nom de la servlet dans le fichier web.xml. Ce nom sert d’alias à la classe d’implémentation ;

<servlet-class> contient le nom complet de la classe d’implémentation ;

<init-param> contient la définition d’un paramètre qui ne sera disponible que pour la servlet par l’intermédiaire de la méthode getInitparameter(...). Cet élément est facultatif et peut apparaître plusieurs fois. Il contient lui­même le nom du paramètre dans <param-name> et sa valeur dans <param-value>.

les associations de la servlet avec les URLs dans l’élément <servlet-mapping> :

<servlet-name> contient le nom de la servlet tel qu’il a été défini dans le même élément au sein de <servlet> ;

<url-pattern> contient l’URL. Elle peut contenir des caractères génériques, comme *, par exemple *.do prendra en compte toutes les URLs finissant en .do (action.do).

Quand cette application Web est déployée, nous pouvons invoquer la servlet par l’URL (nous verrons, dans la suite de ce chapitre, comment le nom de l’application chap5 a été paramétré) : Saisissez : http://localhost:8080/chap5/hello

Testez l’URL : http://localhost:8080/chap5/hello?nom=toto.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

Le paramètre d’URL a été récupéré dans la servlet par le code :

...String nom = request.getParameter("nom");...

2. Les JSP

Comme nous avons pu le voir précédemment, les servlets sont des composants qui peuvent devenir vite très lourds, peu lisibles et peu maintenables, du fait qu’il y ait mélange de code Java et d’HTML. Les JSP (Java Server Page) a donc naturellement suivi les servlets. Ici, le concept de codage est différent. La page JSP est une page HTML dans laquelle vont apparaître des morceaux de code Java, les scriptlets. Une page JSP ressemble à d’autres pages de technologies différentes comme le PHP, ou l’ASP.

<%@ 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>Insert title here</title> </head> <body> <%!

int additionner(int a, int b)

return a+b;

String[] villes = "Paris","Nantes","Marseilles";

%>

<h3>Bonjour tout le monde !!!</h3> Le résultat de l’addition est <%=additionner(2,6) %>

<br /> Liste des villes : <ul> <%

for(int i=0 ; i<villes.length ; i++)

out.println("<li>"+villes[i]+"</li>");

%>

</ul> </body> </html>

Dans cette page HTML, nous avons :

des déclarations entourées par <%! ... %> ;

des scriptlets <% ... %> ;

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

des expressions <%= ... %>.

La lisibilité peut s’en trouver améliorée, si le code Java n’est pas trop présent dans la page. Pour améliorer la lisibilité et enlever le code Java des pages JSP, d’autres technologies sont venues s’inscrire dans les JSP. Nous ne ferons que citer certaines d’entre elles, et vous pourrez les apercevoir au détour de certains exemples :

la bibliothèque JSTL pour Java Standard TagLib, qui est une librairie d’éléments XML utilisés dans les pages JSP afin de remplacer les scriptlets ;

l’EL, pour Expression Language, qui permet de remplacer les expressions ;

les JSF, pour Java Server Face, qui sont des composants côtés serveur générant le code HTML, le code Java et le JavaScript nécessaires à la construction de la page envoyée au navigateur.

a. Cycle de vie d’une JSP

Alors qu’une servlet est codée en Java, compilée et déployée dans le répertoire WEB­INF/classes, une JSP est entièrement prise en charge par le conteneur. Elle va être traduite en une classe Java, puis compilée. Lorsqu’une requête demande une ressource JSP, le conteneur vérifie si la classe correspondante à la JSP est chargée en mémoire, si ce n’est pas le cas, ou si la JSP a changé, alors il y a traduction puis compilation de la page JSP.

La traduction Java et le fichier compilé se trouvent dans le répertoire de travail :

<configuration serveur>\work\jboss.web\localhost\<nom appli web>\org\apache\jsp

Par exemple, dans notre cas : \server\default\work\jboss.web\localhost\chap5\org\apache\jsp

La page JSP index.jsp, présentée précédemment, donne après traduction, le fichier source Java : index_jsp.java.

package org.apache.jsp;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.jsp.*;

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent int additionner(int a, int b) return a+b; String[] villes = "Paris","Nantes","Marseille"; private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory(); private static java.util.List _jspx_dependants; private javax.el.ExpressionFactory _el_expressionfactory; private org.apache.AnnotationProcessor _jsp_annotationprocessor; public Object getDependants() return _jspx_dependants; public void _jspInit() _el_expressionfactory = _jspxFactory.getJspApplicationContext (getServletConfig().getServletContext()).getExpressionFactory(); _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache. AnnotationProcessor.class.getName()); public void _jspDestroy() public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; Object page = this; JspWriter _jspx_out = null; PageContext _jspx_page_context = null; try response.setContentType("text/html; charset=ISO-8859-1"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("\n"); out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"); out.write("<html>\n"); out.write("<head>\n"); out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n"); out.write("<title>Insert title here</title>\n"); out.write("</head>\n");

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

out.write("<body>\n"); out.write("\r\n"); out.write("\r\n"); out.write("<h3>Bonjour tout le monde !!!</h3>\r\n"); out.write("\r\n"); out.write("Le résultat de l’addition est "); out.print(additionner(2,6) ); out.write("\r\n"); out.write("<br />\r\n"); out.write("Liste des villes :\r\n"); out.write("<ul>\r\n"); for(int i=0 ; i<villes.length ; i++) out.println("<li>"+villes[i]+"</li>"); out.write("\r\n"); out.write("</ul>\r\n"); out.write("\n"); out.write("</body>\n"); out.write("</html>"); catch (Throwable t) if (!(t instanceof SkipPageException)) out = _jspx_out; if (out != null && out.getBufferSize() != 0) try out.clearBuffer(); catch (java.io.IOException e) if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); finally _jspxFactory.releasePageContext(_jspx_page_context);

Le code HTML a produit un code qui envoie les chaînes de caractères HTML vers le flux de sortie, dans des invocations out.write(...).

out.write("<body>\n"); out.write("\r\n"); out.write("\r\n"); out.write("<h3>Bonjour tout le monde !!!</h3>\r\n");

Le code présent dans la partie déclaration de la page JSP, a été traduit par des membres de classe :

Dans la page JSP :

<%! int additionner(int a, int b)

return a+b;

String[] villes = "Paris","Nantes","Marseille"; %>

Dans la classe :

int additionner(int a, int b) return a+b; String[] villes = "Paris","Nantes","Marseille";

La fonction JSP additionner(...) a été traduite par une méthode addtionner(...). La variable de page JSP villes a été traduite par la propriété villes.

L’expression JSP est évaluée et envoyée dans le flux de sortie.

Dans la page JSP :

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

<%=additionner(2,6) %>

Dans la classe :

out.print(additionner(2,6) );

Les scriptlets ont été transposées telles quelles dans la classe.

Dans la page JSP :

<% for(int i=0 ; i<villes.length ; i++)

out.println("<li>"+villes[i]+"</li>"); %>

Dans la classe :

for(int i=0 ; i<villes.length ; i++) out.println("<li>"+villes[i]+"</li>");

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz8e5MRKSRzIgLAA==-enidentnumber

Les descripteurs de déploiement

Il y a deux descripteurs de déploiement. Le descripteur de déploiement standard le fichier web.xml, et le descripteur de déploiement spécifique, le fichier jboss­web.xml. Le descripteur de déploiement standard est portable d’un serveur d’application à l’autre, le descripteur de déploiement spécifique est propre au serveur JBoss.

Le descripteur standard contient la configuration de l’application web, des références aux ressources. Le descripteur de déploiement spécifique contient la liaison entre les ressources référencées dans le fichier web.xml et leur déploiement dans le serveur d’application.

Les descripteurs de déploiement sont situés dans le répertoire WEB­INF de l’application Web.

1. Descripteur de déploiement standard web.xml

L’élément racine <web-app> du fichier web.xml comprend les éléments fils suivants :

<icon>, <display-name>, <description> qui sont des éléments utilisés par certains outils de gestion de déploiement, pour afficher une icône, un nom et une description associés à l’application Web.

<distribuable> permet de marquer l’application comme distribuable sur plusieurs serveurs, ce qui permet d’exploiter la répartition de charge contrôlée par le serveur.

<context-param> qui contient la valeur des paramètres utilisables dans toute l’application. Ces paramètres, définis dans le fichier web.xml, peuvent être utilisés dans les servlets et les pages JSP. Les paramètres sont déclarés dans les balises <param-name> et <param-value>.

<filter> et <filter-mapping> qui permettent de déclarer des filtres sur des URL. Les filtres vont permettre de faire des traitements avant que la requête n’arrive à la servlet ou à la JSP, ou après que la réponse soit générée. Ce type de classe doit implémenter l’interface javax.servletFilter.

<listener> qui permet de déclarer des écouteurs sur des événements : entre autres, démarrage et retrait de l’application web, début et fin de session.

<servlet> et <servlet-mapping> qui permettent de déclarer les servlets et de les associer avec des URL.

<session-config> qui contient l’élément <session-timeout> permettant de définir le temps maximal d’une session, en minutes. Le temps par défaut de 30 minutes est défini dans le fichier server\default\deploy\jboss­web.deployer\conf\web.xml. Vous pouvez régler ce temps, application par application.

<mime-mapping> qui reprend les types MIME, ces types sont configurés dans le web.xml par défaut.

<welcome-file-list> qui déclare la liste des noms des pages d’accueils par défaut, si aucune ressource n’est demandée dans l’URL.

<error-page> définit les pages d’erreur à renvoyer en cas de code erreur HTTP, ou d’exception Java.

<error-code> contient le code erreur HTTP, par exemple 404 ;

<exception-type> contient le nom de la classe d’exception ;

<location> contient le chemin de la page d’erreur à afficher.

<security-constraint>, <security-role>, <login-config> permettent de mettre en place des sections du site qui sont soumises à des autorisations d’accès. Nous aurons l’occasion de revenir plus en détail sur l’utilisation de ces balises, dans le chapitre Gestion de la sécurité consacré à la sécurité.

<resource-ref>, <ejb-ref> déclarent des références vers des ressources, ou des EJB, qui sont déployées sur le serveur. Ces déclarations seront associées aux ressources réelles dans le fichier de déploiement spécifique jboss­web.xml.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzMTWxVKSRzIgLAA==-enidentnumber

2. Descripteur de déploiement spécifique jboss­web.xml

Les ressources déclarées dans le fichier web.xml par l’intermédiaire des éléments <resource­ref>, font référence au nommage JNDI de l’application, dans le contexte java:com/env. Les ressources réelles sont nommées dans contexte JNDI plus global. Il serait dommage que les servlets et JSP, si elles utilisent des ressources, soient obligées de connaître les noms JNDI de plus haut niveau, cela nuirait à la portabilité. Le fichier jboss­web.xml va permettre d’associer les contextes JNDI.

L’élément racine de ce fichier est <jboss-web>. Nous allons découvrir ce fichier en l’illustrant par quelques exemples.

a. Nom de l’application

<jboss-web> <context-root>chap5</context-root> </jboss-web>

L’élément <context-root> permet de préciser le nom de l’application web. Si ce nom n’est pas précisé, l’application sera nommée avec le nom du fichier war.

Si notre archive s’appelle "Chap 5 ­ Web.war", le nom de l’application déployée sera "Chap 5 ­ Web" qui correspondra à l’URL http://localhost:8080/Chap 5 ­ Web/.

Si une valeur est donnée à l’élément <context-root>, le nom de l’application correspondra à cette valeur, dans notre cas : http://localhost:8080/chap5/.

b. Association de ressources

Dans le fichier web.xml, la ressource est déclarée de la manière suivante :

<web-app> ... <resource-ref> <res-ref-name>jdbc/Bovoyage</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> ... </web-app>

Dans la servlet, la récupération de la ressource est effectuée par un code ressemblant à cela :

InitialContext context = new InitialContext();

DataSource ds = (DataSource)context.lookup("java:comp/env/jdbc/Bovoyage");

Dans le fichier jboss­web.xml, l’association est effectuée de la manière suivante :

<jboss-web> ... <resource-ref> <res-ref-name>jdbc/Bovoyage</res-ref-name> <jndi-name>java:jdbc/BovoyageDS</jndi-name> </resource-ref> ...

Association d’une source de données

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzMTWxVKSRzIgLAA==-enidentnumber

</jboss-web>

Si nous interrogeons la liste des noms JNDI, nous pouvons voir que l’association a été effectuée :

Contexte JNDI java: :

java: Namespace +- jaas (class: javax.naming.Context) | +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext) | +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext) | +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext) +- TransactionPropagationContextImporter (class: com.arjuna.ats.internal. jbossatx.jta.PropagationContextManager) +- comp.ejb3 (class: javax.naming.Context) | NonContext: null +- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl) +- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource) +- StdJMSPool (class: org.jboss.jms.asf.StdServerSessionPoolFactory) +- TransactionManager (class: com.arjuna.ats.jbossatx.jta. TransactionManagerDelegate) +- test_seamDatasource (class: org.jboss.resource.adapter.jdbc. WrapperDataSource) +- TransactionPropagationContextExporter (class: com.arjuna.ats.internal. jbossatx.jta.PropagationContextManager) +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory) +- jdbc (class: org.jnp.interfaces.NamingContext) | +- BovoyageDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)

Contexte JNDI de l’application Web :

java:comp namespace of the Chap 5 - Web.war application: +- UserTransaction[link -> UserTransaction] (class: javax.naming.LinkRef) +- env (class: org.jnp.interfaces.NamingContext) | +- unEntier (class: java.lang.Integer) | +- jdbc (class: org.jnp.interfaces.NamingContext) | | +- Bovoyage[link -> java:jdbc/BovoyageDS] (class: javax.naming. LinkRef)

Nous constatons donc que les contextes JNDI ont bien été effectués. Il est important de travailler selon ce procédé, plutôt que d’aller, dans l’application Web, chercher dans les contextes JNDI de haut niveau. En effet cela permet d’assurer une meilleure portabilité entre les serveurs d’applications.

Dans ce type d’association, nous pouvons utiliser des EJB situés :

sur le même serveur, donc les interfaces locales seront utilisées ;

sur un autre serveur, et alors les interfaces distantes sont utilisées.

Dans le fichier web.xml, les références locales sont mises en place par l’élément <ejb-local-ref> et les références distantes le sont par <ejb-ref>.

<web-app> ... <ejb-ref> <ejb-ref-name>ejb/calc</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home> fr.eni.editions.jboss.ejb2.calculette.CalculetteHome </home> <remote> fr.eni.editions.jboss.ejb2.calculette.Calculette </remote> </ejb-ref>

Association avec un EJB

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzMTWxVKSRzIgLAA==-enidentnumber

<ejb-local-ref> <ejb-ref-name>ejb/calcH</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local-home> fr.eni.editions.jboss.ejb2.calculette.CalculetteLocalHome </local-home> <local> fr.eni.editions.jboss.ejb2.calculette.CalculetteLocal </local> </ejb-local-ref> </web-app>

<ejb-ref-name> contient la valeur utilisée dans la recherche dans le contexte JNDI.

Cette recherche produirait un code ressemblant à celui­ci, pour l’EJB local :

InitialContext context = new InitialContext();

CalculetteLocalHome home = (CalculetteLocalHome)context.lookup("java:comp/env/ejb/calcH"); CalculetteLocal bean = home.create();

Ou produirait un code ressemblant à celui­ci, pour l’EJB distant :

InitialContext context = new InitialContext(); CalculetteHome home = (CalculetteHome)context.lookup(("java:comp/env/ejb/calc"); Calculette bean = home.create();

Le fichier jboss­web.xml met en place les associations de contexte :

<jboss-web> ... <ejb-ref> <ejb-ref-name>ejb/calc</ejb-ref-name> <jndi-name>ejb2/calculette/remote</jndi-name> </ejb-ref> <ejb-local-ref> <ejb-ref-name>ejb/calcH</ejb-ref-name> <local-jndi-name> ejb2/calculette/local </local-jndi-name> </ejb-local-ref> </jboss-web>

Si nous interrogeons la liste des noms JNDI, nous pouvons voir que l’association a été effectuée :

Dans le contexte JNDI global :

+- ejb2 (class: org.jnp.interfaces.NamingContext) | +- calculette (class: org.jnp.interfaces.NamingContext) | | +- local (proxy: $Proxy60 implements interface fr.eni.editions.jboss. ejb2.calculette.CalculetteLocalHome) | | +- remote (proxy: $Proxy62 implements interface fr.eni.editions. jboss.ejb2.calculette.CalculetteHome,interface javax.ejb.Handle)

Dans le contexte JNDI de l’application Web :

java:comp namespace of the Chap 5 - Web.war application: +- UserTransaction[link -> UserTransaction] (class: javax.naming.LinkRef) +- env (class: org.jnp.interfaces.NamingContext) | +- unEntier (class: java.lang.Integer) | +- jdbc (class: org.jnp.interfaces.NamingContext) | | +- Bovoyage[link -> java:jdbc/BovoyageDS] (class: javax.naming. LinkRef)

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzMTWxVKSRzIgLAA==-enidentnumber

| +- ejb (class: org.jnp.interfaces.NamingContext) | | +- calc[link -> ejb2/calculette/remote] (class: javax.naming. LinkRef) | | +- calcH[link -> ejb2/calculette/local] (class: javax.naming. LinkRef)

Accès à l’environnement.

Des valeurs peuvent être mises en contexte JNDI par l’intermédiaire de l’élément <env-entry> dans le fichier web.xml.

<web-app> ... <env-entry> <env-entry-name>unEntier</env-entry-name> <env-entry-type>java.lang.Integer</env-entry-type> <env-entry-value>25</env-entry-value> </env-entry> </web-app>

<env-entry-name> est le nom JNDI dans le contexte de l’application ;

<env-entry-type> est le type de l’entrée. Cela peut être java.lang.Boolean, java.lang.Integer, java.lang.Double, java.lang.Float ou java.lang.String ;

<env-entry-value> est la valeur de l’entrée.

Cela se traduit par la mise en place dans le contexte JNDI de l’entrée :

java:comp namespace of the Chap 5 - Web.war application: +- UserTransaction[link -> UserTransaction] (class: javax.naming.LinkRef) +- env (class: org.jnp.interfaces.NamingContext) | +- unEntier (class: java.lang.Integer)

Et la recherche de l’entrée pourrait être un code ressemblant à celui­ci :

Integer entier = null;

try

InitialContext ctx = new InitialContext();

entier = (Integer)ctx.lookup("java:comp/env/unEntier"); System.out.println("<<< valeur de l’entier : "+entier); catch(Exception e)

System.out.println("<<< erreur : "+e);

Les recherches dans un contexte JNDI peuvent être remplacées par de l’injection de ressources avec Tomcat 6. La version de Tomcat utilisée ici par JBoss est la 5.5.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzMTWxVKSRzIgLAA==-enidentnumber

Les hôtes virtuels

Les hôtes virtuels permettent d’associer plusieurs noms de domaine avec une application Web. Le nom de domaine doit être enregistré auprès d’une autorité DNS. Mais vous pouvez aussi forcer un nom de domaine en modifiant le fichier hosts. Ce fichier est situé :

sous Ubuntu, dans /etc/hosts ;

sous Windows, dans C:\WINDOWS\system32\drivers\hosts.

127.0.0.1 localhost 127.0.0.1 www.toto.net 127.0.0.1 www.titi.net

Des domaines et des adresses IP sont associés dans ce fichier. Ici, il y deux nouveaux domaines associés à la machine. Lors de l’invocation des domaines www.titi.net et www.toto.net, l’adresse 127.0.0.1 sera réellement invoquée.

Le fichier server.xml, situé sous <répertoire jboss>/server/default/conf, a été modifié pour ajouter les lignes suivantes dans la balise <Engine> :

<Engine> ... <Host name="toto" autoDeploy="false" deployOnStartup="false" deployXML="false" configClass="org.jboss.web.tomcat.security.config.JBossContextConfig"> <Alias>www.toto.net</Alias> <Alias>www.titi.net</Alias> <Valve className="org.jboss.web.tomcat.service.jca.CachedConnectionValve" cachedConnectionManagerObjectName="jboss.jca:service=CachedConnectionManager" transactionManagerObjectName="jboss:service=TransactionManager" /> </Host> <Engine>

La balise <Host> représente un hôte virtuel. Elle possède les attributs suivants :

name : le nom de l’hôte virtuel, habituellement un nom réseau ;

appBase : chemin relatif ou absolu où se trouvent les applications Web, par défaut le répertoire de déploiement ;

autoDeploy : auto déploiement des applications, ici à false car un service JBoss déploie les applications et non pas Tomcat ;

deployOnStartup : déploiement automatique au démarrage ;

deployXML : désactivation des fichiers XML de contexte ;

configClass : classe de gestion de la configuration.

Les balises <Alias> permettent d’ajouter des alias au nom réseau défini dans l’attribut name de <Host>.

La balise <Valve> permet de définir des éléments qui viendront s’insérer dans le traitement des requêtes.

Dans le fichier jboss­web.xml, nous pouvons associer un hôte virtuel défini dans server.xml avec notre application Web, et un nom d’application.

<jboss-web> <context-root>/</context-root> <virtual-host>toto</virtual-host>

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzkVXvZKSRzIgLAA==-enidentnumber

</jboss-web>

L’élément <context-root> contient "/" qui est l’application par défaut. Par ce biais, nous avons associé notre application aux URL :

http://toto:8080/

http://www.toto.net:8080/

http://www.titi.net:8080/

Mais vous pouvez vérifier que l’URL http://localhost:8080/ pointe toujours vers la page d’accueil de JBoss.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzkVXvZKSRzIgLAA==-enidentnumber

Configuration du conteneur d’EJB

Comme nous avons pu le voir lors des rappels sur les EJB au chapitre Introduction, le client d’un EJB ne crée jamais directement d’instance d’EJB, et n’invoque jamais directement une méthode sur l’EJB. C’est le conteneur d’EJB du serveur JBoss qui est chargé d’effectuer ces appels.

Pour un EJB2 par exemple, il est de la responsabilité du serveur de fournir les javax.ejb.EJBHome et javax.ejb.EJBObject pour un EJB, le client référencera EJBHome pour tout ce qui a trait au cycle de vie de l’EJB et EJBObject pour les invocations des méthodes métier.

Côté client, les phases de déploiement d’un EJB2 et la fabrication des proxies par le serveur peuvent être résumées par les étapes suivantes :

1. Le moteur de déploiement d’EJB (org.jboss.ejb.EJBDeployer) déploie l’archive JAR de l’EJB. Un module EJB (org.jboss.ejb.EJBModule) est alors créé.

2. Lors de la phase de création du module EJB, une fabrique de proxy (org.jboss.ejb.EJBFactory) gère la création des proxies des interfaces Home et Object de l’EJB.

3. La fabrique de proxy crée un proxy logique composé d’un proxy dynamique (java.lang.reflect.Proxy), des interfaces Home de l’EJB, d’une implémentation d’un ProxyHandler (java.lang.reflect.InvocationHandler) et des classes d’interception. Les interfaces homes de l’EJB sont liées au contexte JNDI.

4. Lorsque le client recherche dans un contexte JNDI l’interface Home de l’EJB, le proxy associé est transporté dans la machine virtuelle du client.

Lorsque le client invoque une méthode de l’EJB, il y a création d’une instance de la classe org.jboss.invocation.Invocation qui encapsule la demande du client. Cette invocation passe au travers d’une chaîne de classes d’interception. L’ensemble des classes associées à la fabrique des proxies et liées à la chaîne d’interception des invocations sont décrites dans le fichier <répertoire du serveur>/conf/standardjboss.xml dont voici un extrait :

<invoker-proxy-binding> <name>stateless-unified-invoker</name> <invoker-mbean>jboss:service=invoker,type=unified</invoker-mbean> <proxy-factory>org.jboss.proxy.ejb.ProxyFactory</proxy-factory> <proxy-factory-config> <client-interceptors> <home> <interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value= "false">org.jboss.invocation.InvokerInterceptor</interceptor> <interceptor call-by-value= "true">org.jboss.invocation.MarshallingInvokerInterceptor</interceptor> </home> <bean> <interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor </interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value="false">org.jboss.invocation. InvokerInterceptor</interceptor> <interceptor call-by-value="true">org.jboss.invocation. MarshallingInvokerInterceptor</interceptor> </bean> </client-interceptors> </proxy-factory-config> </invoker-proxy-binding>

Le nom de la classe de fabrique des proxies se retrouve dans la balise <proxy-factory>. Puis la description de la chaîne d’interception pour les interfaces de gestion du cycle de vie et d’exposition des méthodes métier de l’EJB se retrouve dans les balises <home> et <bean>.

Nous voyons que nous avons des intercepteurs pour :

les interfaces : org.jboss.proxy.ejb.HomeInterceptor et org.jboss.proxy.ejb.StatelessSessionInterceptor ;

Côté client

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz22ulzaSRzIgLAA==-enidentnumber

le contexte sécurité : org.jboss.proxy.SecurityInterceptor ;

les transactions : org.jboss.proxy.TransactionInterceptor ;

le mode de transport de la requête du client : .

par référence, c’est­à­dire dans la même machine virtuelle, avec org.jboss.invocation.InvokerInterceptor ;

par valeur, c’est­à­dire par RMI, avec org.jboss.invocation.MarshallingInvokerInterceptor.

L’invocation d’un EJB se termine côté serveur. L’invocation d’une méthode de l’EJB va être transportée vers la machine virtuelle de JBoss, via le bus JMX.

Chaque proxy est associé à une classe d’invocation et à un protocole de transport.

Le service EJBDeployer est responsable de la création des conteneurs EJB.

Nous pouvons retrouver ce service dans la console de visualisation des services, à l’adresse : http://localhost:8080/jmx­console/ puis, en recherchant le lien service=EJBDeployer sous la rubrique jboss.ejb.

Vous pouvez alors voir les attributs du MBean sur l’écran suivant :

Côté serveur

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz22ulzaSRzIgLAA==-enidentnumber

Parmi les attributs modifiables :

VerifyDeployments : vérifie que l’EJB est conforme aux spécifications EJB2.1. Il est très utile pour s’assurer de la validité du déploiement. Le résultat de la vérification n’affecte pas le déploiement lui­même, voir StrictVerifier.

VerifierVerbose : passe la vérification du déploiement en mode "bavard".

StrictVerifier : si cet attribut est positionné à "true", le déploiement n’est pas effectué si la vérification a échoué.

CallByValue : mise en place par défaut, des invocations avec passage des paramètres par valeur.

ValidateDTDs : vérifie que les fichiers XML ejb­jar.xml et jboss.xml sont valides par rapport à leur DTD.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz22ulzaSRzIgLAA==-enidentnumber

Déploiement des EJB 2

Le composant EJB2 est packagé dans un module EJB (archive en .jar). Ce module peut être déployé directement, ou être lui­même inclus dans une application d’entreprise (archive en .ear).

1. Packaging

a. Packaging en module EJB

Le répertoire META­INF comporte :

le fichier de déploiement standard de l’EJB : ejb­jar.xml ;

le fichier de déploiement spécifique pour JBoss : jboss.xml.

Le fichier de déploiement standard sera portable d’un serveur à l’autre, tandis que le fichier de déploiement spécifique est propre à JBoss. Ce fichier spécifique sera donc à réécrire en cas de déploiement sous un autre serveur d’application. Heureusement, ces fichiers ont une syntaxe très proche les uns des autres.

b. Packaging dans une application d’entreprise

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Le module application d’entreprise (fichier ear) peut regrouper :

des modules Web (fichiers war) ;

des modules EJB (fichiers jar) ;

des librairies java (fichiers jar) ;

un répertoire META­INF contenant le fichier application.xml qui permet de déclarer les modules et de surcharger les déploiements par défaut.

c. Déploiement

Le déploiement est effectué très simplement en plaçant le module EJB, ou l’application d’entreprise, dans le répertoire deploy du serveur. Par exemple, si vous travaillez sur le serveur par défaut, le répertoire de déploiement sera : <répertoire installation JBoss>/server/default/deploy.

Ce répertoire est normalement le répertoire de déploiement à chaud par défaut. Il est géré par le MBean org.jboss.deployment.scanner.URLDeploymentScanner. Nous avons vu au chapitre Architecture de JBoss comment nous pouvions modifier le répertoire de déploiement.

2. Descripteurs de déploiements

Les descripteurs de déploiement sont au format XML.

Le répertoire <répertoire installation JBoss>/docs/dtd contient l’ensemble des fichiers DTD (Document Type Definition) utilisés par JBoss.

Il y a deux types de descripteurs de déploiement :

les standards qui sont conformes aux spécifications de Sun ;

les spécifiques qui sont propres au serveur d’application.

Par exemple, les spécifications J2EE expliquent comment faire référence et utiliser une source de données, une authentification dans un module, mais ne spécifient pas comment les mettre en place et comment les lier avec le module. Cette mise en place de la source de données et la liaison avec le module sont propres à chaque serveur.

a. Descripteur de déploiement standard ejb­jar.xml

Vous pouvez télécharger le schéma XML correspondant au fichier ejb­jar.xml à l’URL suivante : http://java.sun.com/xml/ns/j2ee/#resources

Ce fichier XML est portable d’un serveur à l’autre. Il permet de constituer le composant, en y décrivant :

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

les classes interface ;

la classe d’implémentation ;

le type d’EJB ;

les configurations de gestion de l’EJB ;

la déclaration des champs persistants pour les EJB entités.

Nous ne reprendrons ici que les éléments principaux. Les exemples de déploiement qui suivent dans ce chapitre vont permettre d’illustrer ce fichier ejb­jar.xml.

<ejb-jar> : racine du document XML ;

<enterprise-beans> : contient les éléments fils <session>, <entity>, <message-driven> correspondant aux différents EJB du module ;

<session> : déclaration d’un EJB de type session ;

<entity> : déclaration d’un EJB de type entité, pour la persistance ;

<message-driven> : déclaration d’un EJB de type orienté message ;

<ejb-name> : nom de l’EJB, c’est ce nom qui sera utilisé dans le fichier jboss.xml ;

<home> : nom de l’interface qui étend l’interface javax.ejb.EJBHome, celle­ci présentant les méthodes du cycle de vie utilisables à distance par RMI ;

<local-home> : nom de l’interface qui étend l’interface javax.ejb.EJBLocalHome, présentant les méthodes du cycle de vie utilisables en local, c’est­à­dire dans la même machine virtuelle ;

<remote> : nom de l’interface qui étend l’interface javax.ejb.EJBObject, présentant les méthodes métier utilisables à distance par RMI ;

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<local> : nom de l’interface qui étend l’interface javax.ejb.EJBLocalObject, présentant les méthodes métier utilisables en local, c’est­à­dire dans la même machine virtuelle ;

<ejb-class> : nom de la classe d’implémentation de l’EJB, qui étend une des classes javax.ejb.SessionBean, javax.ejb.EntityBean ou javax.ejb.MessageDrivenBean en fonction du type d’EJB ;

<relationships> : décrit les relations entre les EJB entités.

b. Descripteur de déploiement spécifique jboss.xml

Le fichier jboss.xml est le fichier de déploiement spécifique à JBoss. En fonction de la version de JBoss que vous utilisez, vous pouvez référencer des DTD différentes.

<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd"> <jboss> ... </jboss>

Vous retrouverez l’ensemble des DTDs disponibles dans le répertoire <répertoire installation JBoss>/docs/dtd.

Chaque fichier jboss.dtd commence par un commentaire présentant la structure du fichier jboss.xml.

Pour le fichier jboss.dtd, nous avons la structure suivante :

<jboss> <secure /> <security-domain /> <enterprise-beans> <entity> <ejb-name /> <jndi-name /> <resource-ref> <res-ref-name /> <resource-name /> </resource-ref> </entity> <session> <ejb-name /> <jndi-name /> <resource-ref> <res-ref-name /> <resource-name /> </resource-ref> </session> </enterprise-beans> <resource-managers> <resource-manager> <res-name /> <res-jndi-name /> </resource-manager> <resource-manager> <res-name /> <res-url /> </resource-manager>

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

</resource-managers> <container-configurations> <container-configuration> <container-name /> <container-invoker /> <container-interceptors /> <instance-pool /> <instance-cache /> <persistence-manager /> <transaction-manager /> <container-invoker-conf /> <container-cache-conf /> <container-pool-conf /> <commit-option /> <role-mapping-manager/> <authentication-module/> </container-configuration> </container-configurations> </jboss>

Cette structure de base s’est enrichie au fur et à mesure de l’évolution des versions de JBoss.

Les exemples de déploiement exposés dans ce chapitre permettront d’illustrer l’utilisation de ce fichier.

Dans le fichier jboss.xml, nous allons pouvoir, entre autres, y préciser :

les nommages JNDI des EJB ;

les références à des ressources, comme les sources de données...

Seuls les éléments les plus utilisés seront ici décrits. Certains éléments, comme l’élément <secure> ont disparu avec les versions supérieures des DTD. Il convient donc toujours de bien vérifier les versions qui sont utilisées.

<enterprise-beans> encapsule les balises <session>, <entity> et <message-driven>. L’ensemble de ces balises correspond aux balises de même nom dans le fichier ejb­jar.xml en y ajoutant des paramètres supplémentaires spécifiques à JBoss. Nous avons donc en général, autant d’entrées d’EJB de type session entité ou orienté message dans les deux fichiers ejb­jar.xml et jboss.xml.

<ejb-name> est un élément fils des éléments <session>, <entity> et <message-driven>. Il contient le nom de l’EJB qui doit être le même que dans le fichier ejb­jar.xml.

<jndi-name> est un élément fils des éléments <session>, <entity>. Il contient le nom de JNDI de l’interface Home distante de l’EJB.

<local-jndi-name> est un élément fils des éléments <session>, <entity>. Il contient le nom de JNDI de l’interface Home locale de l’EJB.

<ejb-ref> est un élément fils des éléments <session>, <entity> et <message-driven>. Il contient la référence vers un autre EJB distant.

c. Descripteur de déploiement application.xml

Le plus simple pour déployer un ensemble de composants sur un même serveur est de regrouper l’ensemble de ces composants dans une même archive. Cette archive est un fichier dont l’extension est ear, pour enterprise archive.

Cette archive comporte dans son répertoire META­INF, un descripteur de déploiement, le fichier application.xml, dont la racine est <application>.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Le fichier XML suivant présente un exemple typique de ce fichier. Ce fichier est extrait du projet BovoyageCMP_EAR que nous retrouverons plus loin dans ce chapitre.

<?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.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd id="Application_ID" version="5"> <module> <ejb>Bovoyage_CMP.jar</ejb> </module> <module> <web> <web-uri>BovoyageWeb_CMP.war</web-uri> <context-root>bovoyage-cmp</context-root> </web> </module> </application>

Nous y retrouvons la déclaration d’un module EJB :

<module> <ejb>Bovoyage_CMP.jar</ejb> </module>

et d’un module Web auquel nous avons défini un nom d’application :

<module> <web> <web-uri>BovoyageWeb_CMP.war</web-uri> <context-root>bovoyage-cmp</context-root> </web> </module>

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

3. Outils d’aide au codage des EJB 2

Les composants EJB 2 peuvent vite devenir complexes à écrire.

Ainsi, pour créer une EJB2 de type session, le développeur doit créer les 5 classes nécessaires à notre EJB, et les fichiers XML :

l’interface qui étend javax.ejb.EJBHome ;

l’interface qui étend javax.ejb.EJBlocalHome ;

l’interface qui étend javax.ejb.EJBObject ;

l’interface qui étend javax.ejb.EJBLocalObject ;

la classe d’implémentation qui étend javax.ejb.SessionBean ;

le fichier ejb­jar.xml ;

le fichier jboss.xml.

XDoclet est une bibliothèque open­source qui via, des balises de documentation spécifiques, générera les fichiers java et xml adaptés à la cible choisie. Dans notre cas, cette cible sera de l’EJB 2 avec comme serveur de déploiement JBoss.

Vous trouverez dans le chapitre Produits supplémentaires une présentation succincte de l’utilisation de XDoclet.

XDoclet allège le processus de création. Le développeur n’a qu’à créer la classe d’implémentation et paramétrer les balises XDoclet, l’outil générera l’ensemble des fichiers nécessaires, ainsi que des classes d’aide (helper) :

une classe xxxUtil, dans notre cas CalculetteUtil, qui possède un certain nombre de méthodes permettant d’encapsuler la recherche des interfaces Home et LocalHome via JNDI ;

une classe xxxSession , dans notre cas CalculetteSession, qui hérite de notre classe d’implémentation et de javax.ejb.SessionBean. C’est cette classe qui sera référencée dans le fichier ejb­jar.xml. Pour les autres types d’EJB, le nom de la classe est différent : xxxCMP pour les entités CMP par exemple ;

une classe xxxData de type POJO (Plain Old Java Object), pour les EJB de type entité, qui pourra être utilisée avec des objets de transfert.

4. EJB2 de session

Les exemples qui illustrent les EJB2 sont disponibles dans l’archive téléchargeable sur le site ENI. Ces exemples sont une vue simplifiée d’une application d’entreprise. Entre autres, un certain nombre de modèles de conception, que nous utilisons dans des réalisations réelles, ne sont pas présents ici pour ne pas obscurcir la lisibilité du code : pas d’objets de transferts (TO pour Transfer Object), une couche DAO simplifiée, des façades simplifiées, absence de classe de localisation (Locator).

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

De même, la partie Web n’utilise pas de framework comme Struts ou autre, mais en garde l’esprit en utilisant un contrôleur.

Cet exemple simule le choix d’un voyage sur une agence en ligne.

Les EJB de cet exemple ont été générés avec XDoclet. Seules les classes EJB se terminant par Bean (CaddieBean, DestinationFacadeBean...) ont été écrites. Les interfaces et classes helpers, ainsi que les fichiers de déploiement ejb-jar.xml et jboss.xml, ont été générés par un script Ant utilisant XDoclet. L’utilisation de XDoclet dans Eclipse est automatisée, le chapitre Produits supplémentaires abordera ce sujet.

Cet ouvrage n’est pas centré sur les EJB 2 ou EJB 3, il n’y aura donc pas une présentation en profondeur de ces frameworks. Les EJB 2 sont présentés avec les fichiers xml minimum pour que le lecteur qui ne connaît pas cette technologie, ne soit pas perdu s’il la rencontre. Les EJB 3 ont supplanté les EJB 2 dans les nouveaux projets. Mais il n’est pas rare qu’un développeur, administrateur ou chef de projet soit amené à intervenir ou à déployer des EJB 2 sur des projets plus anciens, c’est la raison pour laquelle ils sont présentés dans cet ouvrage.

Les EJB de session sont des composants qui vont être utilisés par une application cliente, distante ou non, mais dont les états ne seront pas persistés en base de données. Tandis que par définition, les EJB entités sont persistants, et donc leur état est sauvegardé en base de données.

Les EJB de session sont utilisés pour effectuer des traitements liés à des requêtes du client, ou gérer des objets de session.

Les EJB de session peuvent être sans état (stateless), ou avec état (statefull).

L’EJB sans état ne conserve pas son état, d’un appel à l’autre d’un client, alors que l’EJB avec état, le conserve. Une instance d’EJB sans état peut être utilisée par plusieurs clients, alors que l’instance d’une EJB avec état est utilisée par un seul client.

Il faut noter que ce n’est pas parce que le client demande la création d’un EJB par la méthode create(...) sur son interface Home, qu’il y a instanciation d’un EJB. Le serveur peut gérer des pools d’instances et recycler les instances qui ne sont pas utilisées. De même, le serveur peut être amené à sérialiser un EJB de session, et donc à le désérialiser. Attention, ce n’est pas parce qu’il y a sérialisation, qu’il y a persistance. La sérialisation est un mécanisme de gestion des instances interne au serveur. La persistance, quant à elle, permet de sauvegarder l’état d’un objet en base de données.

Les méthodes du cycle de vie de l’EJB, qui sont exposées par le biais des interfaces Home, sont appelées par le serveur.

De même les méthodes métier ne sont jamais directement invoquées par le client, elles le sont par le serveur si toutes les conditions de transactions, d’autorisations sont satisfaites.

Quelques exemples d’EJB stateless :

un calcul d’intérêt ;

la récupération du code postal d’une ville.

Quelques exemples d’EJB statefull :

un panier de commande ;

un suivi de paiement.

a. stateless

Comme vu précédemment, un EJB sans état n’est pas associé à un client donné. Une même instance d’EJB stateless peut être utilisée par plusieurs requêtes de clients. Une seule requête est exécutée à la fois, les EJB n’étant pas multithreadés.

Le cycle de vie d’un EJB sans état est très simple. Son instance est existante ou pas.

Cycle de vie

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Comme illustration d’un EJB sans état, nous prendrons l’EJB DestinationFacadeBean du projet Bovoyage_CMP. Cet EJB permet à une application cliente de récupérer un certain nombre de données sur les destinations. Cet EJB rend des services à l’application cliente, mais ne conserve aucun état entre deux requêtes du client. Il s’agit donc bien d’un EJB sans état.

La méthode ejbCreate() est codée dans la classe héritant de javax.ejb.SessionBean.

Remarquez que cette méthode renvoie void.

public abstract class DestinationFacadeBean implements javax.ejb.SessionBean

... public void ejbCreate()

...

La méthode create() est exposée dans la classe de type javax.ejb.EJBHome. Cette méthode renvoie un proxy sur l’interface distante javax.ejb.EJBObject, qui implémente les méthodes métier accessibles via RMI.

public interface DestinationFacadeHome extends javax.ejb.EJBHome

public org.bovoyage.ejb2.DestinationFacade create() throws javax.ejb.CreateException,java.rmi.RemoteException;

La méthode create() est aussi exposée dans la classe de type javax.ejb.EJBLocalHome. Cette méthode renvoie un proxy sur l’interface locale javax.ejb.EJBLocalObject, qui implémente les méthodes métier accessibles au sein de la même machine virtuelle.

public interface DestinationFacadeLocalHome extends javax.ejb.EJBLocalHome

public DestinationFacadeLocal create() throws javax.ejb.CreateException;

Notez que les exceptions susceptibles d’être levées sont différentes : l’exception java.rmi.RemoteException n’est levée que s’il s’agit de méthodes distantes.

Les méthodes métier sont exposées par les interfaces de type javax.ejb.EJBObject pour les méthodes accessibles via RMI, et javax.ejb.EJBLocalObject pour les méthodes accessibles localement, au sein de la même machine virtuelle. Ces méthodes sont implémentées dans la classe héritant de javax.ejb.SessionBean.

Méthodes du cycle de vie

Méthodes métier

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Extraits des classes :

public interface DestinationFacade extends javax.ejb.EJBObject

public java.util.List getDestinations( ) throws java.rmi.RemoteException; public java.util.List getSejours(String idDestination ) throws java.rmi.RemoteException; ...

public interface DestinationFacadeLocal

extends javax.ejb.EJBLocalObject

public java.util.List getDestinations( ) ;

public java.util.List getSejours(String idDestination ) ;

...

public abstract class DestinationFacadeBean implements javax.ejb.SessionBean

... public List getDestinations()

ArrayList destinations = new ArrayList();

try

DestinationLocalHome home = DestinationUtil.getLocalHome();

Collection liste = home.findAll(); Iterator it = liste.iterator(); while (it.hasNext())

DestinationLocal local = (DestinationLocal) it.next(); DestinationData data = new DestinationData(local.getId(),

local.getPays(), local.getDescription()); destinations.add(data); catch (Exception e)

// TODO Auto-generated catch block

e.printStackTrace(); return destinations;

...

Pour cet EJB stateless, le descripteur de déploiement standard ejb-jar.xml contient les lignes suivantes :

<session id="Session_DestinationFacade"> <description><![CDATA[An EJB named DestinationFacade]]></description> <display-name>DestinationFacade</display-name>

Descripteur ejb­jar.xml

- 10 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<ejb-name>DestinationFacade</ejb-name>

<home>org.bovoyage.ejb2.DestinationFacadeHome</home> <remote>org.bovoyage.ejb2.DestinationFacade</remote> <local-home> org.bovoyage.ejb2.DestinationFacadeLocalHome </local-home> <local>org.bovoyage.ejb2.DestinationFacadeLocal</local> <ejb-class> org.bovoyage.ejb2.DestinationFacadeSession </ejb-class> <session-type>Stateless</session-type>

<transaction-type>Container</transaction-type> </session>

L’élément <session> qui indique que l’EJB décrit est de type session contient ici les éléments suivants :

<description> et <display-name> sont utilisés par certains outils de gestion d’EJB au sein d’un serveur. Ils peuvent être omis.

<ejb-name> contient le nom de l’EJB. C’est ce nom qui sera repris dans le fichier jboss.xml.

<home> contient le nom de l’interface qui étend javax.ejb.EJBHome, exposant les méthodes distantes du cycle de vie.

<remote> contient le nom de l’interface qui étend javax.ejb.EJBObject, exposant les méthodes métier distantes.

<local-home> contient le nom de l’interface qui étend javax.ejb.EJBLocalHome, exposant les méthodes locales du cycle de vie.

<local> contient le nom de l’interface qui étend javax.ejb.EJBLocalObject, exposant les méthodes métier locales.

<ejb-class> contient le nom de la classe d’implémentation des méthodes du cycle de vie et des méthodes métier. Cette classe implémente l’interface javax.ejb.SessionBean.

<session-type> indique le type d’EJB de session, ici un EJB sans état.

<transaction-type> indique ici que les transactions sont gérées par le conteneur.

Pour cet EJB, le descripteur de déploiement spécifique jboss.xml contient les lignes suivantes :

<session> <ejb-name>DestinationFacade</ejb-name> <jndi-name>ejb2/DestinationFacade</jndi-name> <local-jndi-name>ejb2/local/DestinationFacade</local-jndi-name> </session>

L’élément <session> du fichier jboss.xml est le pendant du même élément du fichier ejb­jar.xml. Il permet de préciser le déploiement dans le serveur JBoss. Nous y trouvons ici :

<ejb-name> qui contient le nom de l’EJB tel qu’il a été défini dans la balise <ejb-name> du fichier ejb­jar.xml.

<jndi-name> contient le nom distant de l’EJB, c’est ce nom qui sera utilisé par les clients distants, via RMI. L’EJB est mis dans le contexte JNDI le plus élevé du serveur pour pouvoir être accessible en dehors du contexte serveur.

Descripteur jboss.xml

- 11 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<local-jndi-name> contient le nom local de l’EJB. C’est ce nom qui sera utilisé par les clients qui sont dans la même machine virtuelle que l’EJB, donc au sein du même serveur. Ce nom fait aussi parti du contexte JNDI le plus élevé, mais il ne peut pas être utilisé par un client distant.

b. statefull

L’instance de l’EJB statefull est liée au client qui l’utilise, l’état de cet EJB peut donc être sérialisé. Ainsi, le cycle de vie de l’EJB est un peu plus complexe, puisque l’instance peut exister mais être "passiver".

Comme illustration d’un EJB avec état, nous prendrons l’EJB CaddieBean du projet Bovoyage_EJB2. Cet EJB reflète le contenu du panier d’achat d’un utilisateur, c’est donc bien un EJB statefull.

Ce type d’EJB peut posséder plusieurs méthodes de création. Il faut veiller à ce qu’il y ait autant de méthodes createXxxx(...) et create(...) exposées par les interface Home et LocalHome, que de méthodes ejbCreateXxxx(...) et ejbCreate(...) dans la classe d’implémentation.

public abstract class CaddieBean implements javax.ejb.SessionBean

private SessionContext ctx;

private List sejours = new ArrayList();

... public void ejbCreateAvecSejour(SejourData sejour)

this.add(sejour);

public void ejbCreate(SejourData sejour)

this.add(sejour);

public void ejbCreate()

public void setSessionContext(SessionContext arg0) throws

EJBException, RemoteException this.ctx = arg0;

...

Cycle de vie

Méthodes du cycle de vie

- 12 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Notez que la classe d’implémentation met à jour sa propriété ctx de type javax.ejb.SessionContext, lors de l’invocation par le conteneur de la méthode setSessionContext(...).

Voici les signatures dans l’interface javax.ejb.EJBLocalHome, notez qu’elles ne sont pas susceptibles de générer une exception de type java.rmi.RemoteException, puisque les valeurs sont transmises par référence.

public interface CaddieLocalHome

extends javax.ejb.EJBLocalHome

public CaddieLocal createAvecSejour(SejourData sejour)

throws javax.ejb.CreateException;

public CaddieLocal create(SejourData sejour)

throws javax.ejb.CreateException;

public CaddieLocal create()

throws javax.ejb.CreateException;

Voici les signatures dans l’interface javax.ejb.EJBHome :

public interface CaddieHome

extends javax.ejb.EJBHome

public Caddie createAvecSejour(SejourData sejour)

throws javax.ejb.CreateException,java.rmi.RemoteException;

public Caddie create(SejourData sejour)

throws javax.ejb.CreateException,java.rmi.RemoteException;

public Caddie create()

throws javax.ejb.CreateException,java.rmi.RemoteException;

Les méthodes métier sont exposées par les interfaces de type javax.ejb.EJBObject pour les méthodes accessibles via RMI, et javax.ejb.EJBLocalObject pour les méthodes accessibles localement, au sein de la même machine virtuelle. Ces méthodes sont implémentées dans la classe héritant de javax.ejb.SessionBean.

Extraits des classes :

public interface Caddie

extends javax.ejb.EJBObject

public void add(SejourData sejour )

throws java.rmi.RemoteException;

public void remove(SejourData sejour )

throws java.rmi.RemoteException;

public double getPrix( )

throws java.rmi.RemoteException;

...

public interface CaddieLocal

extends javax.ejb.EJBLocalObject

public void add(SejourData sejour ) ;

Méthodes métier

- 13 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

public void remove(SejourData sejour ) ;

public double getPrix( ) ;

...

public abstract class CaddieBean implements javax.ejb.SessionBean

private SessionContext ctx;

private List sejours = new ArrayList();

public void add(SejourData sejour)

if(!sejours.contains(sejour))

sejours.add(sejour); public void remove(SejourData sejour)

if(sejours.contains(sejour))

sejours.remove(sejour); public double getPrix()

double prix=0;

Iterator it = sejours.iterator(); while(it.hasNext())

SejourData s = (SejourData)it.next(); prix += s.getPrix().doubleValue(); return prix;

...

Pour cet EJB statefull, le descripteur de déploiement standard ejb-jar.xml contient les lignes suivantes :

<session id="Session_Caddie"> <description><![CDATA[An EJB named Caddie]]></description> <display-name>Caddie</display-name> <ejb-name>Caddie</ejb-name> <home>org.bovoyage.ejb2.CaddieHome</home> <remote>org.bovoyage.ejb2.Caddie</remote> <local-home>org.bovoyage.ejb2.CaddieLocalHome</local-home> <local>org.bovoyage.ejb2.CaddieLocal</local> <ejb-class>org.bovoyage.ejb2.CaddieSession</ejb-class> <session-type>Stateful</session-type> <transaction-type>Container</transaction-type> </session>

L’élément <session> qui indique que l’EJB décrit est de type session contient ici les éléments suivants :

<description> et <display-name> sont utilisés par certains outils de gestion de EJB au sein d’un serveur. Ils

Descripteur ejb­jar.xml

- 14 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

peuvent être omis.

<ejb-name> contient le nom de l’EJB. C’est ce nom qui sera repris dans le fichier jboss.xml.

<home> contient le nom de l’interface qui étend javax.ejb.EJBHome, exposant les méthodes distantes du cycle de vie.

<remote> contient le nom de l’interface qui étend javax.ejb.EJBObject, exposant les méthodes métier distantes.

<local-home> contient le nom de l’interface qui étend javax.ejb.EJBLocalHome, exposant les méthodes locales du cycle de vie.

<local> contient le nom de l’interface qui étend javax.ejb.EJBLocalObject, exposant les méthodes métier locales.

<ejb-class> contient le nom de la classe d’implémentation des méthodes du cycle de vie et des méthodes métier. Cette classe implémente l’interface javax.ejb.SessionBean.

<session-type> indique le type d’EJB de session, ici un EJB avec état. Attention à l’orthographe de Stateful qui ne prend ici qu’une lettre "l" finale.

<transaction-type> indique ici que les transactions sont gérées par le conteneur.

Pour cet EJB, le descripteur de déploiement spécifique jboss.xml contient les lignes suivantes :

<session> <ejb-name>Caddie</ejb-name> <jndi-name>ejb2/Caddie</jndi-name> <local-jndi-name>ejb2/local/Caddie</local-jndi-name> </session>

L’élément <session> du fichier jboss.xml est le pendant du même élément du fichier ejb­jar.xml. Il permet de préciser le déploiement dans le serveur JBoss. Nous y trouvons ici :

<ejb-name> qui contient le nom de l’EJB tel qu’il a été défini dans la balise <ejb-name> du fichier ejb­jar.xml.

<jndi-name> contient le nom distant de l’EJB, c’est ce nom qui sera utilisé par les clients distant, via RMI. L’EJB est mis dans le contexte JNDI le plus élevé du serveur pour pouvoir être accessible en dehors du contexte serveur.

<local-jndi-name> contient le nom local de l’EJB. C’est ce nom qui sera utilisé par les clients qui sont dans la même machine virtuelle que l’EJB, donc au sein du même serveur. Ce nom fait aussi partie du contexte JNDI le plus élevé, mais il ne peut pas être utilisé par un client distant.

5. EJB2 entité

L’EJB entité représente un objet persistant dont l’état sera donc synchronisé avec un enregistrement en base de données. C’est un objet qui possède en plus des méthodes, des données qui correspondent à une réalité du métier. Cette réalité existe en dehors de tout traitement, par exemple un compte client, l’identité d’une personne, un voyage, etc.

Le conteneur d’EJB est chargé de retrouver un EJB entité en utilisant les données stockées en base. À chaque enregistrement en base correspond une instance d’EJB entité différente. Lorsque l’état de l’EJB entité change, le conteneur synchronise le contenu de la base de données.

Le conteneur doit connaître les associations entre l’entité et la base de données. Pour cela, le mapping décrit comment les propriétés de l’entité sont enregistrées dans une colonne d’une table de la base de données.

Descripteur jboss.xml

- 15 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Il existe deux types d’EJB entité :

les CMP pour Container Managed Persistence. Ce type d’EJB est complètement pris en charge par le conteneur d’EJB. C’est le conteneur qui assure la persistance des données, grâce aux descripteurs de déploiement.

les BMP pour Bean Managed Persistence. C’est le bean qui assure la persistance des données. Le développeur écrit le code de persistance.

L’unicité d’un EJB entité est liée à la clé primaire qui est, par définition, unique. Cette clé est toujours la même dans tout le cycle de vie de l’EJB. Cette notion de clé primaire est primordiale car le conteneur ne connait que cette propriété (qui est immuable pour lui) pour identifier, de manière sûre, un EJB entité.

Pour que les états d’un EJB entité soient persistés, il faut que les propriétés liées aux états soient sérialisables.

Un EJB entité représentant un ensemble de données provenant du système d’information, il peut être sollicité simultanément par plusieurs clients. Dans notre exemple, une entité qui représente une destination précise peut être consultée par plusieurs utilisateurs. Les EJB entité sont donc soumis à une concurrence. C’est le conteneur qui gère cette concurrence.

Cette gestion de la concurrence est importante lorsque deux utilisateurs veulent modifier le même EJB entité. Imaginons que sur une destination d1, la secrétaire veuille modifier le champ pays pour corriger une faute de frappe, et qu’au même instant, le responsable commercial modifie la description de la destination.

La concurrence apparaît si plusieurs utilisateurs utilisent une même entité simultanément. Elle n’apparaît pas si les utilisateurs utilisent le bean entité de manière successive et ceci même si les utilisateurs référencient le bean de manière continue.

Concurrence

Réentrance

- 16 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

La réentrance est le fait que, lors de l’exécution d’un code sollicitant des EJB entité ou session, il est possible d’y retrouver un même EJB deux fois. Le bean est sollicité une première fois par un appel de méthode, puis du fait des appels successifs de méthodes, il sera sollicité une seconde fois. Si vous entrez dans le bean une seconde fois, il y a réentrance.

Ici, la méthode setPrix() sur s1 invoque la méthode mettreAJourPrix() sur d1, alors que la méthode setPrix() de d1 n’est pas terminée. Il n’y a pas réentrance si les méthodes sont appelées successivement.

Il n’est pas possible par défaut de faire de la réentrance sur les JEB. L’élément <reentrant> du fichier ejb­jar.xml permet de modifier cette politique.

a. CMP

Le cycle de vie d’un EJB entité est plus complexe que celui d’un EJB session. En effet, en plus des méthodes de type create(), il y a toutes les méthodes de recherche d’un EJB en base de données, en fonction de sa clé primaire. C’est le conteneur qui gère ces recherches, qui vont déboucher sur la création d’une instance. Il s’agit donc de méthodes de création d’objet. Ces méthodes de recherches sont du type findXxxx(...), elles sont appelées méthodes "finder". Ces méthodes sont déclarées dans les interfaces de type javax.ejb.EJBLocalHome et javax.ejb.EJBHome. Il faut noter que pour respecter l’encapsulation de la couche DAO par rapport aux applications clientes, l’accès aux finders est souvent effectué par des EJB de session. Il en résulte que souvent, seules les interfaces de type Local sont utilisées.

Cycle de vie

- 17 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Le conteneur appelle les différentes méthodes du cycle de vie. En l’occurrence, avant l’appel d’une méthode métier, le conteneur va synchroniser le CMP avec la base de données. Ensuite, il va appeler la méthode ejbLoad(), le lien entre l’EJB et l’enregistrement étant la clé primaire.

Si certaines propriétés du CMP ne sont pas persistantes, comme des valeurs calculées, le développeur utilisera la méthode ejbLoad() pour mettre à jour ces propriétés.

Il faut noter que le développeur ne fournit que des classes abstraites. Le conteneur créera les classes concrètes, qui étendront les classes du développeur. Le développeur ne fournit pas non plus les variables d’instances qui stockent l’état du CMP.

Les méthodes du cycle de vie sont exposées via l’interface Home, ici javax.ejb.LocalHome. Notez que les méthodes de recherche font partie des méthodes du cycle de vie.

public interface DestinationLocalHome

extends javax.ejb.EJBLocalHome

public DestinationLocal create()

throws javax.ejb.CreateException;

public Collection findAll()

throws javax.ejb.FinderException;

public DestinationLocal findByPays(String nom)

throws javax.ejb.FinderException;

public DestinationLocal findByPrimaryKey(Integer pk)

throws javax.ejb.FinderException;

La classe d’implémentation qui implémente javax.ejb.EntityBean ne fournit pas d’implémentation pour les méthodes de recherche. Ces méthodes "finders" seront décrites dans le fichier ejb­jar.xml. Le conteneur se chargera de la création des méthodes. Nous avons les implémentations classiques des méthodes du cycle de vie create(), load(), etc.

Méthodes du cycle de vie

- 18 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Extraits de la classe d’implémentation :

public abstract class DestinationBean implements javax.ejb.EntityBean

private EntityContext ctx = null;

public java.lang.Integer ejbCreate()

throws javax.ejb.CreateException

return null;

public void ejbPostCreate() throws javax.ejb.CreateException

public void setEntityContext(EntityContext arg0)

throws EJBException,RemoteException

this.ctx = arg0;

public void unsetEntityContext()

throws EJBException, RemoteException

this.ctx=null;

En général, les méthodes métier sont principalement composées des getters et setters. Les méthodes sont ici exposées par l’interface DestinationLocal qui étend javax.ejb.EJBLocalObject.

public interface DestinationLocal

extends javax.ejb.EJBLocalObject

public java.lang.Integer getId( ) ;

public void setId(Integer id ) ;

public java.lang.String getPays( ) ;

public void setPays(String pays ) ;

public java.lang.String getDescription( ) ;

public void setDescription(String description ) ;

public java.util.Collection getSejours( ) ;

public void setSejours(Collection sejours ) ;

L’implémentation consiste uniquement à fournir des méthodes abstraites.

public class DestinationBean

implements javax.ejb.EntityBean

public abstract java.lang.Integer getId( ) ;

public abstract void setId(Integer id ) ;

public abstract java.lang.String getPays( ) ;

public abstract void setPays(String pays ) ;

public abstract java.lang.String getDescription( ) ;

Méthodes métier

- 19 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

public abstract void setDescription(String description ) ;

public abstract java.util.Collection getSejours( ) ;

public abstract void setSejours(Collection sejours ) ;

Visualisons d’abord l’extrait du fichier ejb­jar.xml correspondant à la déclaration des EJB entités.

<entity id="ContainerManagedEntity_Sejour"> <ejb-name>Sejour</ejb-name> <local-home>org.bovoyage.ejb2.cmp.SejourLocalHome</local-home> <local>org.bovoyage.ejb2.cmp.SejourLocal</local> <ejb-class>org.bovoyage.ejb2.cmp.SejourCMP</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.Integer</prim-key-class> <reentrant>false</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>Sejour</abstract-schema-name> <cmp-field id="CMPAttribute_1"> <field-name>id</field-name> </cmp-field> <cmp-field Id="CMPAttribute_2"> <field-name>depart</field-name> </cmp-field> <cmp-field id="CMPAttribute_3"> <field-name>retour</field-name> </cmp-field> <cmp-field id="CMPAttribute_4"> <field-name>prix</field-name> </cmp-field> <primkey-field>id</primkey-field> <query> <query-method> <method-name>findAll</method-name> <method-params> </method-params> </query-method> <ejb-ql><![CDATA[SELECT OBJECT(a) FROM Sejour as a]]></ejb-ql> </query> </entity> <entity id="ContainerManagedEntity_Destination"> <ejb-name>Destination</ejb-name> <local-home>org.bovoyage.ejb2.cmp.DestinationLocalHome</local-home> <local>org.bovoyage.ejb2.cmp.DestinationLocal</local> <ejb-class>org.bovoyage.ejb2.cmp.DestinationCMP</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.Integer</prim-key-class> <reentrant>false</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>Destination</abstract-schema-name> <cmp-field id="CMPAttribute_5"> <field-name>id</field-name> </cmp-field> <cmp-field id="CMPAttribute_6"> <field-name>pays</field-name> </cmp-field> <cmp-field id="CMPAttribute_7"> <field-name>description</field-name> </cmp-field> <primkey-field>id</primkey-field> <query> <query-method> <method-name>findAll</method-name>

Descripteur ejb­jar.xml

- 20 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<method-params></method-params> </query-method> <ejb-ql><![CDATA[SELECT OBJECT(a) FROM Destination as a]]> ....</ejb-ql> </query> </entity>

L’élément <session> qui indique que les EJB décrits sont de type entité, contient ici, les éléments suivants :

<description> et <display-name> sont utilisés par certains outils de gestion de EJB au sein d’un serveur. Ils peuvent être omis.

<ejb-name> contient le nom de l’EJB. C’est ce nom qui sera repris dans le fichier jboss.xml.

<home> contient le nom de l’interface qui étend javax.ejb.EJBHome, exposant les méthodes distantes du cycle de vie. Dans l’exemple, il n’y a pas d’interface Home distante.

<remote> contient le nom de l’interface qui étend javax.ejb.EJBObject, exposant les méthodes métier distantes. Dans l’exemple, il n’y a pas d’interface Object distante.

<local-home> contient le nom de l’interface qui étend javax.ejb.EJBLocalHome, exposant les méthodes locales du cycle de vie.

<local> contient le nom de l’interface qui étend javax.ejb.EJBLocalObject, exposant les méthodes métier locales.

<ejb-class> contient le nom de la classe d’implémentation des méthodes du cycle de vie et des méthodes métier. Cette classe implémente l’interface javax.ejb.EntityBean.

<persistence-type> indique comment le conteneur gère la persistance. Il s’agit ici de CMP, donc la valeur de l’élément est Container, la valeur Bean est réservée pour les entités de type BMP.

<reentrant> indique si la réentrace dans l’EJB est permise ou pas. False indique ici que la réentrance n’est pas permise.

<cmp-version> indique la version du CMP.

<prim-key-class> indique le type Java de la clé primaire.

<abstract-schema-name> : contient le nom du schéma abstrait, qui sera, entre autres, repris dans les requêtes en EJB­QL.

Ensuite, les différentes propriétés à persister sont décrites dans un élément <cmp-field> qui contient l’élément <description> utilisé par certains outils, mais surtout, l’élément <fiel-name> contenant le nom du champ dans le schéma abstrait. Ce nom correspond aux accesseurs de la classe abstraite de l’entité.

Par exemple, le champ prix correspond aux méthodes abstraites setPrix(...) et getPrix().

Nous trouvons ensuite la déclaration des requêtes. Les requêtes correspondent aux méthodes de recherche d’entités, dites "méthodes finders". Elles sont déclarées dans l’interface Home, puisqu’elles concourent à la création d’une instance et donc, font partie du cycle de vie de l’EJB entité.

L’élément <query> comporte les éléments fils :

<query-method> qui décrit le côté Java de la méthode finder.

<method-name> qui est un élément de <query-method> et contient le nom du finder, ici findAll. Ce nom correspond à la méthode findAll() de l’interface Home.

<method-params> qui est un élément de <query-method> et qui décrit les éventuels paramètres à passer à la méthode dans un élément <method-param>. Celui­ci contient le type Java du paramètre et correspondra à un paramètre de requête en EJB­QL symbolisé par ?x, où x est le rang du paramètre dans la requête.

- 21 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<ejb-ql> : contient la requête écrite en EJB­QL. En général, cette requête est contenue dans un CDATA pour éviter l’interprétation des signes >, en effet > peut se trouver dans une clause WHERE de la requête. Par exemple :

<query> <query-method> <method-name>findByPrixInferieurA</method-name> <method-params> <method-param>java.lang.Double</method-param> </method-params> </query-method> <ejb-ql> <![CDATA[SELECT OBJECT(d) FROM Destination AS d WHERE d.prix <?1]]> </ejb-ql> </query>

Voici maintenant l’extrait du fichier ejb­jar.xml correspondant à la description de la relation existant entre les deux EJB entité.

<relationships id="Relationships_1"> <ejb-relation id="EJBRelation_1"> <ejb-relation-name> Destination-to-Sejours </ejb-relation-name> <ejb-relationship-role id="EJBRelationshipRole_1"> <ejb-relationship-role-name> sejour-vers-une-destination </ejb-relationship-role-name> <multiplicity>Many</multiplicity> <relationship-role-source id="RoleSource_1"> <ejb-name>Sejour</ejb-name> </relationship-role-source> <cmr-field id="CMRField_1"> <cmr-field-name>destination</cmr-field-name> </cmr-field> </ejb-relationship-role> <ejb-relationship-role id="EJBRelationshipRole_2"> <ejb-relationship-role-name> destination-vers-plusieurs-sejours </ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source id="RoleSource_2"> <ejb-name>Destination</ejb-name> </relationship-role-source> <cmr-field id="CMRField_2"> <cmr-field-name>sejours</cmr-field-name> <cmr-field-type> java.util.Collection </cmr-field-type> </cmr-field> </ejb-relationship-role> </ejb-relation> </relationships>

La balise <relationships> contient la définition des relations entre entités. Chaque relation est définie par un élément <ejb-relation>. Avant de décrire les éléments de la relation, regardons comment les relations sont exprimées dans le schéma abstrait.

- 22 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Il s’agit d’une relation bidirectionnelle du type "un vers plusieurs". Il existe une collection de Sejours dans Destination et une Destination dans Sejour. Le conteneur doit pouvoir retrouver les données qui correspondent aux deux entités. La description logique d’une relation est effectuée dans l’élément <ejb-relation>. Comme le nom de l’élément l’indique, il s’agit bien de la description de la relation entre deux EJB, et non pas en base de données, qui sera explicitée dans le fichier jbosscmp­jdbc.xml.

La partie déclarant les entités dans le descripteur jboss.xml est assez simple. Nous y retrouvons un élément <entity> qui est dans l’élément père <enterprise-beans>.

... <entity> <ejb-name>Sejour</ejb-name> <local-jndi-name>ejb2/local/Sejour</local-jndi-name> <method-attribute></method-attribute> </entity> <entity> <ejb-name>Destination</ejb-name> <local-jndi-name>ejb2/local/Destination </local-jndi-name> </entity> ...

Nous y retrouvons de manière classique maintenant :

<ejb-name> contient le nom de l’EJB tel qu’il a été défini dans la balise <ejb-name> du fichier ejb­jar.xml.

<jndi-name> contient le nom distant de l’EJB, c’est ce nom qui sera utilisé par les clients distants, via RMI. L’EJB est mis dans le contexte JNDI le plus élevé du serveur pour pouvoir être accessible en dehors du contexte serveur. Cet élément n’est pas utilisé ici.

<local-jndi-name> contient le nom local de l’EJB. C’est ce nom qui sera utilisé par les clients qui sont dans la même machine virtuelle que l’EJB, donc au sein du même serveur.

L’élément <method-attribute> n’est pas utilisé ici, il permet d’optimiser le comportement du conteneur vis­à­vis de certaines méthodes et de leur gestion dans les transactions. Cet élément peut contenir des éléments fils <method> qui contiennent à leur tour :

<method-name> contient le nom d’une méthode, ou un modèle de nom de méthode, comme get*, qui correspond à toutes les getters de l’entité.

<read-only> contient la valeur true qui permettra à JBoss d’éviter les accès concurrents sur la même instance d’entité, lors de l’appel des méthodes marquées en lecture seule.

<transaction-timeout> peut contenir une valeur de limite temporelle sur une transaction, exprimée en secondes.

Ce descripteur permet à JBoss de faire les liaisons entre les EJB entités décrits dans le fichier ejb­jar.xml et les

Descripteur jboss.xml

Descripteur jbosscmp­jdbc.xml

- 23 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

champs en base de données. Ce fichier vient compléter les paramétrages généraux qui sont définis dans le fichier de configuration standardjbosscmp­jbdc.xml, celui­ci étant dans le répertoire conf de la configuration serveur.

<jbosscmp-jdbc> <defaults> <datasource>java:/BovoyageDS</datasource> <datasource-mapping>mySQL</datasource-mapping> <create-table>false</create-table> <alter-table>false</alter-table> <remove-table>false</remove-table> <preferred-relation-mapping> foreign-key </preferred-relation-mapping> </defaults> <enterprise-beans> <entity> ... </entity> <entity> ... </entity> </enterprise-beans> <relationships> <ejb-relation> ... </ejb-relation> </relationships> </jbosscmp-jdbc>

La racine <jbosscmp-jdbc> contient principalement les éléments suivants :

<defaults> contient les valeurs par défaut, utilisées par les EJB entités et les relations. Le fichier standardjbosscmp­jbdc.xml contient une source de données par défaut, qui est DefaultDS. Ici, une nouvelle source de données est mise en place par défaut, pour nos entités et relations. N’oubliez pas que pour que la source de données existe, il faut qu’elle ait été déployée par l’intermédiaire d’un fichier xxx­ds.xml.

<enterprise-beans> contient les EJB entités qui seront à associer à la base de données

<relationships> contient la description des relations entre les entités par rapport à la base de données.

<dependent-value-classes> contient la description de types complexes, autres classes, qui pourraient correspondre à des descriptions de champ contenus dans des éléments <cmp-field>. L’élément <cmp-field> sera étudié plus loin.

<type-mappings> contient le mapping entre les types java, JDBC et SQL. Ce mapping dépend aussi de la base de données. Une liste existe par défaut dans le fichier standardjbosscmp­jbdc.xml.

<reserved-words> contient des éléments <word> permettant de créer une liste de mots réservés. Ces mots réservés ne peuvent pas être utilisés pour nommer des tables ou des champs. Une liste existe par défaut dans le fichier standardjbosscmp­jbdc.xml.

<user-type-mappings> contient les associations de type personnalisées.

<entity-commands> permet de définir des commandes qui pourront être utilisées au sein d’un élement <entity>. Une liste d’entity-command existe dans le fichier standardjbosscmp­jbdc.xml.

Exemple d’entity-command, extrait du fichier standardjbosscmp­jbdc.xml.

<!-- retrieves generated key of the record inserted into hsql db -->

- 24 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<entity-command name="hsqldb-fetch-key" class="org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCHsqldbCreateCommand"> <attribute name="pk-sql">CALL IDENTITY()</attribute> </entity-command>

Exemple d’utilisation de cette entity-command.

<jbosscmp-jdbc> <enterprise-beans> <entity> <ejb-name>LocationEJB</ejb-name> ... <entity-command name="hsqldb-fetch-key"/> </entity> </enterprise-beans> </jbosscmp-jdbc>

Nous allons voir maintenant comment est effectué le mapping entre l’EJB entité et la base de données.

... <entity> <ejb-name>Sejour</ejb-name> <datasource>java:jdbc/BovoyageDS</datasource> <datasource-mapping>mySQL</datasource-mapping> <create-table>false</create-table> <remove-table>false</remove-table> <table-name>sejours</table-name> <cmp-field> <field-name>id</field-name> <read-only>false</read-only> <column-name>kp_sejour</column-name> <jdbc-type>INTEGER</jdbc-type> <sql-type>INTEGER</sql-type> </cmp-field> <cmp-field> <field-name>depart</field-name> <read-only>false</read-only> <column-name>depart</column-name> <jdbc-type>DATE</jdbc-type> <sql-type>DATETIME</sql-type> </cmp-field> ... </entity> ...

L’élément <entity>, contenu dans l’élément <enterprise-beans>, contient principalement :

<ejb-name> contient le nom de l’EJB tel qu’il a été défini dans les fichiers ejb­jar.xml et jboss.xml.

<datasource> contient le nom de la source de données utilisée par cette entité, sinon la source de données par défaut est utilisée.

<datasource-mapping> contient le mapping de type utilisé. Attention de bien respecter l’orthographe qui est déclaré dans le fichier standardjbosscmp­jbdc.xml.

<create-table> contient true ou false qui permet la création de la table si celle­ci n’existe pas.

<remove-table> contient true ou false qui permet la suppression de la table lors du repli de l’application.

- 25 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<table-name> contient le nom de la table utilisée pour le mapping avec l’EJB entité.

<cmp-field> contient la description des associations entre les propriétés de l’entité et les champs de la base de données.

Dans l’élément <cmp-field>, nous trouverons en général :

<field-name> qui contient le nom du champ dans le CMP.

<read-only> qui précise si le champ est en lecture seule ou non.

<column-name> qui contient le nom de la colonne de la table.

<jdbc-type> qui correspond au type JDBC.

<sql-type> qui correspond au type SQL.

Le conteneur d’EJB gère et génère les requêtes SQL. Pour pouvoir les visualiser sur la console, vous pouvez ajouter une configuration pour le framework de suivi des logs : Log4j. L’ajout d’un appender dans le fichier jboss­log4j.xml du répertoire conf de la configuration serveur, permettra de suivre, sur la console, où vers un fichier en modifiant la configuration, les opérations effectuées en base de données.

Dans la liste des appenders présents, vous pouvez ajouter les lignes suivantes :

<appender name="CMP" class="org.apache.log4j.ConsoleAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="Target" value="System.out"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%dABSOLUTE %-5p [%c1] %m%n"/> </layout> </appender>

Ainsi, vous pourrez voir dans la console d’exécution de JBoss, la prise en charge des CMP et les requêtes générées.

13:49:42,218 DEBUG [Sejour] Initializing CMP plugin for Sejour 13:49:42,234 DEBUG [Sejour] Loading standardjbosscmp-jdbc.xml : file:/C:/ SERVEURS/jboss-4.2.2.GA/server/default/conf/standardjbosscmp-jdbc.xml 13:49:42,250 DEBUG [Sejour] jar:file:/C:/SERVEURS/jboss-4.2.2.GA/server/ default/tmp/deploy/tmp1741BovoyageEJB2_EAR.ear-contents/Bovoyage_EJB2.jar!/ META-INF/jbosscmp-jdbc.xml found. Overriding defaults 13:49:42,265 DEBUG [Destination] Initializing CMP plugin for Destination 13:49:42,281 DEBUG [Sejour] Insert Entity SQL: INSERT INTO sejours (kp_sejour, depart, retour, prix, ke_destination) VALUES (?, ?, ?, ?, ?) 13:49:42,281 DEBUG [Sejour] Entity Exists SQL: SELECT COUNT(*) FROM sejours WHERE kp_sejour=? 13:49:42,281 DEBUG [Sejour] entity-command: [commandName=default,commandClass =class org.jboss.ejb.plugins.cmp.jdbc.JDBCCreateEntityCommand,attributes=] 13:49:42,281 DEBUG [Sejour] Remove SQL: DELETE FROM sejours WHERE kp_sejour=? 13:49:42,281 DEBUG [Sejour] Table not create as requested: sejours 13:49:42,281 DEBUG [Sejour#findByPrimaryKey] SQL: SELECT t0_Sejour.kp_sejour FROM sejours t0_Sejour WHERE t0_Sejour.kp_sejour=? 13:49:42,281 DEBUG [Sejour] Added findByPrimaryKey query command for local home interface 13:49:42,281 DEBUG [Sejour#findAll] EJB-QL: SELECT OBJECT(a) FROM Sejour as a 13:49:42,281 DEBUG [Sejour#findAll] SQL: SELECT t0_a.kp_sejour FROM sejours t0_a 13:49:42,281 DEBUG [Destination] Insert Entity SQL: INSERT INTO destinations (kp_destination, pays, description) VALUES (?, ?, ?) 13:49:42,281 DEBUG [Destination] Entity Exists SQL: SELECT COUNT(*) FROM destinations WHERE kp_destination=? 13:49:42,281 DEBUG [Destination] entity-command: [commandName=default, commandClass=class org.jboss.ejb.plugins.cmp.jdbc.JDBCCreateEntityCommand,

Visualiser les appels à la base de données

- 26 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

attributes=] 13:49:42,281 DEBUG [Destination] Remove SQL: DELETE FROM destinations WHERE kp_destination=? 13:49:42,281 DEBUG [Destination] Table not create as requested: destinations 13:49:42,281 DEBUG [Destination#findByPrimaryKey] SQL: SELECT t0_Destination. kp_destination FROM destinations t0_Destination WHERE t0_Destination.kp_ destination=? 13:49:42,281 DEBUG [Destination] Added findByPrimaryKey query command for local home interface 13:49:42,281 DEBUG [Destination#findAll] EJB-QL: SELECT OBJECT(a) FROM Destination as a 13:49:42,281 DEBUG [Destination#findAll] SQL: SELECT t0_a.kp_destination FROM destinations t0_a 13:49:42,281 DEBUG [Destination#findByPays] EJB-QL: SELECT OBJECT(a) FROM Destination AS a WHERE a.pays = ?1 13:49:42,296 DEBUG [Destination#findByPays] SQL: SELECT t0_a.kp_destination FROM destinations t0_a WHERE (t0_a.pays = ?) 13:49:42,296 DEBUG [Sejour] Foreign key constraint not added as requested: relationshipRolename=sejour-vers-une-destination

Vous pouvez y voir la prise en charge des CMP, puis la création des requêtes SQL à partir des requêtes en EJB QL.

b. BMP

Les EJB entité de type BMP (Bean Managed Persistence) demandent un peu plus de travail de la part du développeur. Ils peuvent être utiles lorsque plus de souplesse est demandée à l’entité, et lorsque le langage EJB QL ne suffit pas. La prise en charge par le conteneur, d’un BMP par rapport à un CMP, diffère par le fait que les accès à la base de données sont codés par le développeur. Bien que le développeur code les méthodes, elles seront toujours appelées par le conteneur d’EJB.

Le cycle de vie d’un BMP est très proche du cycle de vie d’un CMP. La grande différence est que le développeur devra fournir le code pour les méthodes de recherches du type findXxxx(...). Ces méthodes sont déclarées classiquement dans les interfaces de type javax.ejb.EJBLocalHome et javax.ejb.EJBHome. Comme pour les CMP, afin de respecter l’encapsulation de la couche DAO par rapport aux applications clientes, l’accès aux finders est souvent effectué par des EJB de session. Il en résulte que souvent, seules les interfaces de type Local sont utilisées.

Cycle de vie

- 27 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

L’interface de type javax.ejb.LocalHome expose les méthodes du cycle de vie. Nous y retrouvons les méthodes de création et de recherche.

public interface ParticipantLocalHome extends EJBLocalHome

public ParticipantLocal create(String civilite,String

nom,String prenom,String email, String passeport,Integer sejourKey) throws CreateException;

public ParticipantLocal create(ParticipantData participant)

throws CreateException;

public ParticipantLocal findByPrimaryKey(Integer id) throws

FinderException; public ParticipantLocal findByEmail(String email) throws

FinderException; public Collection findBySejourKey(Integer sejourKey) throws

FinderException;

Les implémentations des méthodes métier sont plus complexes que pour le CMP, puisque le développeur doit lui­même gérer les requêtes en base de données.

Exemple d’implémentation d’une méthode create(...) :

public class ParticipantBean implements EntityBean

... public Integer ejbCreate(ParticipantData participant) throws

CreateException

Méthodes du cycle de vie

- 28 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Integer kp = null;

this.civilite = participant.getCivilite();

this.nom = participant.getNom();

this.prenom = participant.getPrenom();

this.email = participant.getEmail();

this.passeport = participant.getPasseport();

this.sejourKey = participant.getSejourKey();

try

kp = bmpCreate(); catch (SQLException e)

throw new CreateException("Erreur creation participant \n"+e);

return kp;

public void ejbPostCreate(ParticipantData participant)

public Integer bmpCreate() throws SQLException

String sql = "INSERT INTO participants SET civilite=?,nom=?,prenom=?,email=?,passeport=?,ke_sejour=?"; Integer kp = null;

Connection con = ds.getConnection(); PreparedStatement ps = con.prepareStatement(sql); ps.setString(1, this.civilite);

ps.setString(2, this.nom);

ps.setString(3, this.prenom);

ps.setString(4, this.email);

ps.setString(5, this.passeport);

ps.setInt(6, this.sejourKey.intValue());

ps.executeUpdate(); ResultSet rs = ps.getGeneratedKeys(); if (rs.next())

kp = new Integer(rs.getInt(1));

ps.close(); con.close(); return kp;

...

Exemple d’implémentation d’un finder :

public class ParticipantBean implements EntityBean

... public Integer ejbFindByPrimaryKey(Integer kp) throws FinderException

String sql = "SELECT * FROM participants WHERE kp_participant=?"; Connection con = null;

boolean trouve=false;

try

con = ds.getConnection(); PreparedStatement ps = con.prepareStatement(sql);

- 29 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

ps.setInt(1, kp.intValue()); ResultSet rs = ps.executeQuery(); trouve = rs.next(); con.close(); catch (SQLException e)

throw new EJBException("Erreur recherche primaryKey");

if(trouve)

return this.primaryKey;

else

throw new ObjectNotFoundException("Pas de primaryKey en "+kp);

...

Classiquement, les accesseurs sont exposés dans les interfaces javax.ejb.EJBOject et javax.ejb.EJBLocalObjetc.

public interface ParticipantLocal extends EJBLocalObject

public String getCivilite();

public void setCivilite(String civilite);

public String getNom();

public void setNom(String nom);

public String getPrenom();

public void setPrenom(String prenom);

public String getEmail();

public void setEmail(String email);

public String getPasseport();

public void setPasseport(String passeport);

La classe d’implémentation de type javax.ejb.EntityBean implémente les accesseurs et méthodes métier. Notez que la classe d’implémentation d’un CMP doit utiliser des propriétés d’instance.

public class ParticipantBean implements EntityBean

... private Integer primaryKey;

private Integer sejourKey;

private String civilite;

private String nom;

private String prenom;

private String email;

private String passeport;

... public String getCivilite()

return civilite;

Méthodes métier

- 30 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

public void setCivilite(String civilite)

this.civilite = civilite;

...

Nous retrouvons de manière classique la définition de l’EJB.

...<entity> <ejb-name>Participant</ejb-name> <local-home>org.bovoyage.ejb2.bmp.ParticipantLocalHome</local-home> <local>org.bovoyage.ejb2.bmp.ParticipantLocal</local> <ejb-class>org.bovoyage.ejb2.bmp.ParticipantBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java.lang.Integer</prim-key-class> <reentrant>false</reentrant> ... </entity> ...

La recherche de la source de données de type javax.sql.DataSource est effectuée lors de l’appel de la méthode setEntityContext(...). Il y a recherche dans le contexte JNDI par le nom JNDI, mis en place par le serveur via le fichier de configuration de la source de données.

public class ParticipantBean implements EntityBean ... private final String dsName = "java:jdbc/BovoyageDS";

private DataSource ds; public void setEntityContext(EntityContext arg0) throws EJBException, RemoteException this.ctx = arg0; try InitialContext ct = new InitialContext(); ds = (DataSource) ct.lookup(this.dsName); catch (NamingException e) throw new EJBException(">>> Erreur recherche DataSource", e); ...

Il faut alors ajouter dans le fichier ejb­jar.xml une référence vers une ressource. Ceci est effectué avec l’élément <resource-ref>. Le fichier ejb­jar.xml devient alors le suivant :

<entity> ... <ejb-name>Participant</ejb-name> <local-home>org.bovoyage.ejb2.bmp.ParticipantLocalHome</local-home> <local>org.bovoyage.ejb2.bmp.ParticipantLocal</local> <ejb-class>org.bovoyage.ejb2.bmp.ParticipantBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java.lang.Integer</prim-key-class> <reentrant>false</reentrant> <resource-ref>

<res-ref-name>jdbc/toto</res-ref-name>

Descripteur ejb­jar.xml

- 31 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<res-type>javax.sql.DataSource</res-type>

<res-auth>Container</res-auth>

</resource-ref>

... </entity>

L’élément <resource-ref> accepte les éléments fils suivants :

<res-ref-name> qui correspond au nom de la ressource dans le contexte java:comp/env de l’EJB ;

<res-type> qui contient le nom de la classe correspondant à la ressource, ici javax.sql.DataSource ;

<res-auth> qui demande l’authentification auprès de la ressource par l’intermédiaire du conteneur. Si l’EJB gère l’authentification par programmation, cet élément doit contenir Application. Il faut faire attention dans ce cas aux problèmes de portabilité de l’EJB.

Avec cette configuration, la ressource peut être accessible par le nom JNDI java:comp/env/jdbc/toto au lieu de java:jdbc/BovoyageDS.

Pour que ceci fonctionne, il faut aussi mettre en place, dans le fichier jboss.xml, l’association entre les deux nommages JNDI. C’est ce que nous allons voir maintenant.

Dans le descripteur jboss.xml, la déclaration du BMP dans l’élément <entity> vous est maintenant familière. Vous y retrouvez le nom de l’EJB tel qu’il est défini dans le fichier ejb­jar.xml, et ses associations JNDI. Ici, seule l’interface LocalHome est présente, il n’y a donc pas de nom JNDI pour l’interface de création distante.

<jboss> <enterprise-beans> <entity> <ejb-name>Participant</ejb-name> <local-jndi-name>ejb2/local/Participant</local-jndi-name> </entity> ... <resource-managers> <resource-manager> <res-name>jdbc/toto</res-name> <res-jndi-name>java:jdbc/BovoyageDS</res-jndi-name> </resource-manager> </resource-managers> </jboss>

Pour assurer l’association entre le nom JNDI utilisé dans l’EJB, soit dans l’exemple précédent java:comp/env/jdbc/toto, et le nom JNDI de la ressource telle qu’elle a été enregistrée par JBoss, soit java:jdbc/BovoyageDS, il nous faut ajouter les lignes suivantes au fichier jboss.xml.

<jboss> ... <resource-managers> <resource-manager> <res-name>jdbc/toto</res-name> <res-jndi-name>java:jdbc/BovoyageDS</res-jndi-name> </resource-manager> </resource-managers> ... </jboss>

Ces lignes sont situées en fin de fichier. Les différents alias sont décrits au sein des balises <resource-manager> qui ont, comme élément parent, <resource-managers>.

Descripteur jboss.xml

- 32 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

<res-name> contient le nom de la ressource dans le fichier ejb­jar. Dans le fichier ejb­jar.xml, ce nom est contenu dans la balise <res-ref-name> ;

<res-jndi-name> contient le nom JNDI de la ressource tel qu’il a été monté dans le contexte JNDI par JBoss.

Vérifiez que JBoss est bien en cours d’exécution et que l’archive BovoyageEJB2_EAR est déployée, ouvrez un navigateur et tapez l’URL de la console JMX : http://localhost:8080/jmx­console.

Retrouvez le service d’affichage de l’annuaire de nommage JNDI, c’est­à­dire le lien service=JNDIView dans la rubrique jboss.

Cliquez sur le lien.

Cliquez sur le bouton Invoke de l’opération list() du service.

Vous devez retrouvez le contexte de nommage pour le BMP Participant.

java:comp namespace of the Participant bean:

+- env (class: org.jnp.interfaces.NamingContext) | +- jdbc (class: org.jnp.interfaces.NamingContext) | | +- toto[link -> java:jdbc/BovoyageDS] (class: javax.naming.LinkRef)

Nous voyons que le contexte java:comp/env/jdbc/toto est lié au contexte java:jdbc/BovoyageDS.

Retrouvez le contexte de nommage java: :

java: Namespace

+- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory) +- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource) +- SecurityProxyFactory (class: org.jboss.security.SubjectSecurityProxyFactory) +- DefaultJMSProvider (class: org.jboss.jms.jndi.JNDIProviderAdapter) +- comp (class: javax.naming.Context) +- jdbc (class: org.jnp.interfaces.NamingContext)

| +- BovoyageDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)

+- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl) +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)

Le lien est effectif. Lorsque l’EJB recherchera dans son contexte de nommage la ressource référencée par java:comp/env/jdbc/toto, il recevra la ressource qui y est liée : java:jdbc/BovoyageDS.

L’implémentation de l’EJB est devenu indépendante des nommages des ressources.

6. EJB2 orienté message

Si vous n’êtes pas familier avec les services de messagerie JMS, reportez­vous au chapitre Architecture de JBoss.

Les EJB orientés message, MDB pour Message Driven Bean, sont simples.

Ils n’ont pas d’interface d’exposition de méthodes métier ou de création, donc par de Home, LocalHome, Object, LocalObject.

Ils disposent d’une méthode onMessage().

Ils sont sans état.

Les MDB peuvent être des abonnés permanents ou non. Si le MDB est un abonné durable au service de messagerie, il recevra tous les messages, même si l’EJB orienté message est inactif. Si le MDB n’est pas un abonné permanent, les messages seront perdus si le l’EJB est inactif.

L’intérêt des MDB est de pouvoir envoyer ou recevoir des messages asynchrones vers ou provenant d’applications tiers, sans qu’il y ait de gros impact sur le reste de l’application. D’autres modes de communications avec des applications tiers sont, bien entendu, envisageables, comme les web services par exemple.

Cycle de vie

- 33 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Comme l’EJB session sans état, le cycle de vie du MDB est très simple.

L’implémentation de ces méthodes est ici triviale, le cycle de vie de ce type d’EJB étant très simple.

public class ReceptionConfirmationBean implements

javax.ejb.MessageDrivenBean, javax.jms.MessageListener private javax.ejb.MessageDrivenContext messageContext = null;

public void setMessageDrivenContext(MessageDrivenContext

messageContext) throws javax.ejb.EJBException

this.messageContext = messageContext;

public void ejbCreate()

public void ejbRemove()

messageContext = null;

...

Ici, nous avons juste initialisé la propriété messageContext, de type javax.ejb.MessageDrivenContext. C’est en fait, l’outil XDoclet qui s’est chargé de la tâche.

La méthode onMessage(...) présentée ici est triviale : un simple affichage dans la console du message reçu. Elle sera invoquée par le conteneur d’EJB lors de la réception d’un message.

public class ReceptionConfirmationBean implements

javax.ejb.MessageDrivenBean, javax.jms.MessageListener ... public void onMessage(javax.jms.Message message)

// begin-user-code System.out.println("Message Driven Bean got message " + message); // TODO: do business logic here

Méthodes du cycle de vie

Méthode métier

- 34 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

// end-user-code ...

L’envoi du message est assuré, pour cet exemple, par une application Java indépendante. Cet envoi de message peut être effectué par toute application d’entreprise orientée message, sans lien avec le langage Java et les plates­formes Sun.

public class ConfirmationSejour

private QueueConnectionFactory factory = null;

private InitialContext ctx = null;

public static final String JNDI_QUEUE = "queue/B";

public static final String JNDI_QUEUE_FACTORY = "QueueConnectionFactory";

private Queue queue = null;

public ConfirmationSejour()

try

ctx = new InitialContext();

factory = (QueueConnectionFactory) ctx.lookup (JNDI_QUEUE_FACTORY);

queue = (Queue) ctx.lookup(JNDI_QUEUE);

catch (NamingException e)

System.out.println("ERREUR SUR CONSTRUCTEUR : " + e);

public void envoyerMessage(int numeroSejour)

QueueConnection con = null;

try

con = factory.createQueueConnection(); QueueSession session = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

QueueSender sender = session.createSender(queue); MapMessage message = session.createMapMessage(); Message.setInt("numeroSejour", numeroSejour); message.setString("action", "confirmation"); sender.send(message); System.out.println(">>> MESSAGE ENVOYE : ’" + message + "’"); catch (JMSException jmse)

System.out.println("Erreur lors de l’envoi des

messages : " + jmse.getMessage()); finally

if (con != null)

try

con.close(); catch (JMSException jmse)

- 35 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

public static void main(String[] args)

ConfirmationSejour cl = new ConfirmationSejour();

cl.envoyerMessage(12);

Le descripteur de déploiement précise le nom de l’EJB, la classe utilisée et le mode de gestion par le conteneur. Une seule classe est précisée puisqu’un EJB orienté message ne possède pas d’interface d’exposition des méthodes métier ou de cycle de vie. Attention, il y a des différences notables sur le descripteur d’un EJB orienté message entre les spécifications EJB 2.0 et EJB 2.1. Nous avons ici un descripteur aux spécifications EJB 2.1.

... <message-driven id="MessageDriven_1"> <ejb-name>ReceptionConfirmation</ejb-name> <ejb-class>org.bovoyage.ejb2.mdb.ReceptionConfirmationMdb</ejb-class> <messaging-type>javax.jms.MessageListener</messaging-type> <transaction-type>Container</transaction-type> <message-destination-type> javax.jms.Queue </message-destination-type> <activation-config> <activation-config-property> <activation-config-property-name> destinationType </activation-config-property-name> <activation-config-property-value> javax.jms.Queue </activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name> acknowledgeMode </activation-config-property-name> <activation-config-property-value> Auto-acknowledge </activation-config-property-value> </activation-config-property> </activation-config> </message-driven> ...

L’élément <message-driver> permet d’assembler l’EJB et contient, entre autres, les éléments fils suivants :

<ejb-name> : nom de l’EJB ;

<ejb-class> : classe d’implémentation de l’EJB ;

<messaging-type> : définit la classe fournisseur de message ;

<transaction-type> : transaction gérée par le conteneur ou le bean ;

<message-destination-type> : destination utilisée par l’EJB : sujet (topic) ou file d’attente (queue) ;

<activation-config>.

Le descripteur de déploiement ejb­jar.xml

- 36 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

L’élément <activation-config> a été introduit dans la spécification EJB 2.1. Il remplace les éléments <acknowledge-mode> et <message-selector> de la spécification EJB 2.0. Cet élément est constitué d’une suite d’éléments fils <activation-property> qui permet de définir une propriété. Le nom de cette propriété est défini dans l’élément <activation-config-property-name> et sa valeur dans l’élément <activation-config-property-value>. Les noms des propriétés supportées pour les EJB MDB sont :

acknowledgeMode qui spécifie le mode d’accusé de réception. Remplace la balise <acknowledge-mode>.

messageSelector qui permet de filtrer les messages reçus. Si aucun filtre n’est précisé, l’EJB reçoit tous les messages. Remplace l’élément <message-selector>.

destinationType précise la destination utilisée par le MDB : sujet (topic) ou file d’attente (queue) en indiquant l’interface qui devra être implémentée par la destination concrète. Les interfaces possibles sont : javax.jms.Queue et javax.jms.Topic. Remplace l’élément fils <destination-type> de l’élément <message-driven-destination>.

subscriptionDurability précise l’abonnement à un sujet, il est permanent (Durable) ou on (NonDurable, par défaut). Remplace l’élément fils <subscription-durability> de l’élément <message-driven-destination>.

Le descripteur jboss.xlm est très simple, il précise le nom JNDI de la destination utilisée par l’EJB. De manière classique, le nom de l’EJB est celui défini dans le descripteur ejb­jar.xml.

... <message-driven> <ejb-name>ReceptionConfirmation</ejb-name> <destination-jndi-name>queue/B</destination-jndi-name> </message-driven> ....

Le descripteur de déploiement jboss.xml

- 37 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo+nh76SRzIgLAA==-enidentnumber

Déploiement des EJB 3

La spécification des EJB 3 a nettement simplifié la tâche du développeur. Les points d’amélioration les plus spectaculaires sont :

La simplification du déploiement car le descripteur de déploiement standard ejb­jar.xml a été remplacé par la configuration effectuée par annotation. Le fichier persistence.xml contient le paramétrage des contextes de persistance.

Contrairement aux EJB 2, un paramétrage par défaut est mis en place. Par exemple pour les EJB 2 entité, il fallait déclarer toutes les propriétés à associer aux champs des tables, en EJB 3 les associations existent par défaut.

Écriture des classes facilitées, puisque les EJB 3 s’appuient sur des classes POJO et qu’il n’est plus nécessaire d’utiliser toutes les interfaces du cycle de vie des EJB 2. Un EJB 3 simple peut être écrit avec une seule classe !!!

Utilisation des annotations pour faire de l’injection de dépendance, ce qui permet de faciliter le couplage avec les ressources ou entre les EJB. Attention, l’injection de dépendance du conteneur Web ne fonctionne qu’à partir de Tomcat 6.

Le déploiement des EJB est effectué dans un fichier JAR.

Les fichiers de déploiements ejb­jar.xml et jboss.xml peuvent toujours être utilisés. Attention, les noms des méthodes des EJB ne doivent pas commencer par ejb.

Comme pour les EJB 2, il ne s’agit pas ici de traiter toutes les spécifications se rapportant aux EJB 3, mais de montrer les principes de fonctionnement et de déploiement sous JBoss.

1. Utilisation des annotations

Les annotations utilisées dans les classes des EJB 3 correspondent aux éléments des fichiers de configurations des EJB. Il y a deux sortes d’annotations :

les annotations standard, qui sont définies, pour la plupart, dans le package javax.ejb. Ces annotations sont portables d’un serveur à l’autre.

les annotations spécifiques, qui sont propres à un serveur. Pour JBoss, ces annotations sont dans le package

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

org.jboss.annotation.ejb. Les annotations spécifiques à un serveur ne sont pas prises en compte par les autres serveurs.

@Stateless @LocalBinding(jndiBinding="ejb3/Calculette/local") @RemoteBinding(jndiBinding="ejb3/Calculette/remote") public class CalculetteBean implements CalculetteLocal,

CalculetteRemote

Ici @Stateless est une annotation standard qui indique que la classe est un EJB session sans état, et les annotations @LocalBinding et @RemoteBinding sont des annotations spécifiques à JBoss qui indiquent les noms JNDI sous lesquels sera monté l’EJB.

2. EJB3 de session

La définition d’un EJB de session reste la même que celle que nous avons vu pour les EJB 2. Il s’agit d’un objet qui sera utilisé par un client local ou distant et dont les états ne sont pas persistés en base de données.

Un EJB de session avec état est lié à une session client, il y a une instance d’EJB par client. Un EJB sans état n’est pas lié à un client spécifique.

Les exemples, qui sont présentés dans cette section, sont issus des projets Bovoyage_EJB3 et BovoyageWeb_EJB3 que vous trouverez dans l’archive téléchargeable sur le site ENI.

a. stateless

L’EDB sans état CommandeFacade est ici constitué d’une interface et d’une classe d’implémentation.

L’interface IDestinationFacade ne fait qu’exposer les méthodes métier.

@Local public interface IdestinationFacade

public List<Destination> getDestinations();

public Destination getDestination(long id);

public Sejour getSejour(long id);

L’annotation @Local permet de préciser que cette interface a seulement une visibilité locale seulement. Une visibilité distante est exprimée par la mise en place de l’annotation @Remote. Si deux interfaces différentes sont utilisées pour l’accessibilité des méthodes en local et à distance, il est alors possible d’avoir des méthodes différentes selon le type d’accès. La classe d’implémentation doit évidemment implémenter les deux interfaces.

Les annotations @Local et @Remote peuvent aussi être mises dans la classe d’implémentation. Il faudra, alors, préciser la valeur de l’interface d’exposition des méthodes métier :

@Local(value=IDestinationFacade.class)

La classe d’implémentation code l’interface :

@Stateless @LocalBinding(jndiBinding="ejb3/local/destinationFacade") public class DestinationFacade implements IdestinationFacade

@PersistenceContext(name="bovoyage") EntityManager em; @Override public List<Destination> getDestinations()

Query query = em.createNamedQuery("findAll"); List<Destination> destinations = query.getResultList(); return destinations;

@Override

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

public Destination getDestination(long id)

Destination destination = em.find(Destination.class, id);

return destination;

@Override public Sejour getSejour(long id)

Sejour sejour = em.find(Sejour.class, id);

return sejour;

L’annotation @Stateless marque la classe comme un EJB sans état.

L’annotation JBoss @LocalBinding(jndiBinding="ejb3/local/destinationFacade") permet le paramétrage du nom JNDI sous lequel sera déployé l’EJB.

Nous reviendrons plus loin sur le code des méthodes qui utilisent l’unité de persistance.

Le cycle de vie de l’EJB sans état reste très simple.

Des méthodes callback peuvent être appelées par le conteneur. Ces méthodes sont marquées par les annotations :

@PostConstruct : la méthode sera invoquée après l’injection de dépendance ;

@PreDestroy : la méthode est invoquée lors de la suppression de l’EJB.

Ces méthodes peuvent être mises en place directement dans la classe d’implémentation, ou dans une classe intercepteur.

Implémentation dans la classe :

@Stateless @LocalBinding(jndiBinding="ejb3/local/calculatrice") @Local(value=ICalculatrice.class)

public class Calculatrice implements Icalculatrice

@Override public int additionner(int a, int b)

return a+b;

@PostConstruct

public void debutCalculatrice()

Cycle de vie

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

System.out.println(">>> METHODE POST_CONSTRUCT");

@PreDestroy

public void finCalculatrice()

System.out.println(">>> METHODE PRE_DESTROY");

Mais, mélanger ainsi le code métier avec les méthodes du cycle de vie peut être dommageable pour la lisibilité et donc la maintenance de la classe. Il est donc possible de créer des classes spécifiques qui seront chargées de gérer le cycle de vie : les classes dites Interceptor.

Utilisation d’une classe d’interception :

public class IntercepteurCalculatrice

@PostConstruct public void debutCalculatrice(InvocationContext ctx) throws Exception

System.out.println(">>> AVANT METHODE POST_CONSTRUCT DANS

INTERCEPTEUR"); ctx.proceed(); System.out.println(">>> APRES METHODE POST_CONSTRUCT DANS

INTERCEPTEUR"); @PreDestroy public void finCalculatrice(InvocationContext ctx) throws Exception

System.out.println(">>> AVANT METHODE PRE_DESTROY DANS

INTERCEPTEUR"); ctx.proceed(); System.out.println(">>> APRES METHODE PRE_DESTROY DANS

INTERCEPTEUR"); @AroundInvoke public Object appelMethode(InvocationContext ctx) throws Exception

System.out.println(("INVOCATION DE LA METHODE

"+ctx.getMethod().getName())); return ctx.proceed();

Les méthodes reçoivent un objet de type InvocationContext qui permet d’exécuter la méthode liée au contexte. Ainsi, il sera possible de mettre en place un comportement adapté, avant et après l’appel.

Une annotation @AroundInvoke permet aussi de réagir à l’invocation des méthodes métier, en plus des méthodes du cycle de vie. Il faut dans ce cas renvoyer le résultat de l’invocation.

La classe d’implémentation doit indiquer sa classe Interceptor au moyen de l’annotation @Interceptors.

@Stateless @LocalBinding(jndiBinding="ejb3/local/calculatriceAvecIntercepteur") @Local(value=ICalculatrice.class)

@Interceptors(IntercepteurCalculatrice.class)

public class CalculatriceAvecIntercepteur implements Icalculatrice

@Override public int additionner(int a, int b)

return a+b;

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

Cette annotation peut recevoir une collection de classes, ce qui justifie les accolades.

Il est toujours possible d’utiliser les méthodes ejbCreate(), ejbRemove(). Ces méthodes seront alors traitées par le conteneur, comme si elles avaient été marquées par les annotations @PostConstruct et @PreDestroy.

b. statefull

L’EJB Caddie est, dans notre exemple, un EJB avec état. C’est lui qui conservera les commandes de séjours durant la session avec le client.

L’interface présente ici les méthodes métier accessibles en local.

@Local public interface Icaddie

public void add(Sejour sejour);

public void remove(Sejour sejour);

public double getPrix();

public List<Sejour> getSejours();

public Sejour getSejour(long idSejour);

public Sejour getSejour(String idSejour);

public boolean getVide();

La classe d’implémentation code l’interface. L’annotation @Stateful indique qu’il s’agit d’un EJB de session avec état.

@Stateful @LocalBinding(jndiBinding="ejb3/local/caddie") public class Caddie implements Icaddie

List<Sejour> sejours = new ArrayList<Sejour>();

public void add(Sejour sejour)

if(!sejours.contains(sejour))

sejours.add(sejour); public void remove(Sejour sejour)

if(sejours.contains(sejour))

sejours.remove(sejour); public double getPrix()

double prix=0;

for(Sejour s : sejours)

prix += s.getPrix(); return prix;

public List getSejours()

return sejours;

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

public Sejour getSejour(long idSejour)

Sejour sejour = null;

for(Sejour s : sejours)

if(s.getId()==idSejour)

sejour = s; break;

return sejour;

public Sejour getSejour(String idSejour)

return this.getSejour(Long.parseLong(idSejour));

public boolean getVide()

return this.sejours.size()==0;

Les méthodes callback pouvant être invoquées par le conteneur sont plus nombreuses :

@PostConstruct : la méthode sera invoquée après l’injection de dépendance ;

@PreDestroy : la méthode est invoquée lors de la suppression de l’EJB ;

@PrePassivate : la méthode est appelée avant la passivation de l’EJB, lorsque le conteneur détermine que ce bean peut être sérialisé pour libérer de la mémoire ;

@PostActivate : la méthode est appelée lorsque l’EJB est activé, lorsqu’il revient en mémoire après avoir été passivé.

Cycle de vie

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

3. EJB3 entité

Les EJB 3 représentent une révolution par rapport aux EJB 2 entité. Les frameworks tels Hibernate, ibatis ou Toplink ont largement contribué à l’essor de cette spécification. La gestion de la persistance est gérée par l’API Persistence 1.0 (JSR 220). L’entité est représentée par une classe de type POJO annotée, qui sera ensuite persistée au sein d’une unité de persistance.

Un aspect important dans cette nouvelle spécification est le support de l’héritage entre entités, ce qui implique des associations avec la base de données telles que :

une table unique pour la hiérarchie de classes ;

une table par type concret de classe ;

une table pour les propriétés communes, et une table spécifique pour les propriétés suplémentaires de la classe fille.

a. Mapping avec la base de données

Le mapping des propriétés de la classe avec la base de données est effectué par défaut. Toutes les propriétés sont par défaut persistées. Si la propriété à persister possède le même nom que le champ de la table en base de données, aucune annotation n’est nécessaire.

@Entity @Table(name="destinations") @NamedQueries( @NamedQuery(name="findAll",query="SELECT d FROM Destination as d") ) public class Destination implements Serializable

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy=GenerationType.AUTO)

@Column(name="kp_destination") private long id;

private String pays;

private String description;

@OneToMany(mappedBy="destination", cascade=CascadeType.PERSIST,

fetch=FetchType.EAGER)

private List<Sejour> sejours;

public Destination()

public Destination(String pays, String description)

this.pays = pays;

this.description = description;

// SUIVENT LES GETTERS ET SETTERS ...

Au niveau de la classe, nous y trouvons les annotations suivantes :

@Entity : indique qu’il s’agit d’une entité pouvant être prise en charge par l’unité de persistance ;

@Table : dont l’attribut name indique sur quelle table de la base de données est associée l’entité ;

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

@NamedQuery : qui décrit une requête qui pourra être pré­compilée et qui sera utilisable par l’unité de persistance.

Au niveau des propriétés, nous trouvons les annotations :

@Id : indique qu’il s’agit d’une clé primaire ;

@GeneratedValue va préciser le type de stratégie utilisée pour la génération de la clé ;

@Column précise le nom de la colonne dans la table, si le nom de la propriété est différent du nom de la colonne.

Toutes les propriétés sont par défaut persistées. Si une propriété ne doit pas être sauvegardée, il faut l’annoter avec @Transient.

Notre destination étant constituée de séjours, nous avons une relation entre les deux entités.

Une destination contient une collection de séjours.

Un séjour contient une destination.

La mise en place de la relation est effectuée avec des annotations relationnelles :

@OneToOne lie deux entités avec une relation un à un ;

@OneToMany et @ManyToOne lient une entité unique à une collection, c’est ce que nous avons dans notre exemple ;

@ManyToMany décrit une relation plusieurs vers plusieurs et nécessite une table de liaisons entre les deux tables des entités.

Sur les relations, des opérations en cascade peuvent se produire :

PERSIST : les entités liées sont enregistrées avec l’entité ; par exemple une entité Personne qui possède une collection d’entité Telephone. Les entités Telephone seront enregistrées avec l’entité Personne.

MERGE : les modifications sont effectuées en cascade ;

REMOVE : les suppressions sont effectuées en cascade ;

REFRESH : le chargement est effectué en cascade ;

ALL : cumul des quatre opérations.

Une propriété importante, que nous reverrons dans la partie suivante, est le chargement des objets liés. Notre entité est utilisée dans une servlet, en dehors de l’unité de persistance, aussi, nous avons demandé un chargement immédiat par fetch=FetchType.EAGER.

Côté destination, nous avons la collection de séjour :

@OneToMany(mappedBy="destination", cascade=CascadeType.PERSIST, fetch=FetchType.EAGER) private List<Sejour> sejours;

Cette collection de séjour est liée à la propriété destination de la classe Sejour :

public class Sejour implements Serializable

@Id @Column(name="kp_sejour")

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

@GeneratedValue(strategy=GenerationType.AUTO) private long id;

private Date depart;

private Date retour;

private double prix;

@ManyToOne(cascade=CascadeType.ALL)

@JoinColumn(name="ke_destination")

private Destination destination;

// SUIVENT LES GETTERS ET SETTERS ...

Cette proprété destination est marquée par une relation @ManyToOne (plusieurs séjours pour une destination) et la colonne contenant la clé primaire de la destination dans la table séjour est indiquée par @JoinColumn(name="ke_destination").

b. Unité de persistance

Les entités EJB 3 sont prises en charge par l’unité de persistance, qui possède sa propre spécification "Persistence API 1.0".

L’unité de persistance est caractérisée par :

un ensemble d’EJB entités ;

un fournisseur (provider) de persistance ;

une source de données (DataSource) ;

un gestionnaire d’entités (EntityManager).

Le rôle de l’unité de persistance est :

de retrouver les entités dans la base de données ;

d’assurer l’unicité des instances de chaque entité ;

de gérer le cycle de vie des instances via le gestionnaire d’entités.

Le paramétrage de l’unité de persistance est effectué au moyen du fichier persistence.xml, situé dans le répertoire META­INF de l’archive.

Dans notre exemple, ce fichier est très simple :

<persistence> <persistence-unit name="bovoyage"> <jta-data-source>java:jdbc/BovoyageDS</jta-data-source> </persistence-unit> </persistence>

Nous y trouvons :

la définition de l’unité dans l’élément <persistence-unit> à laquelle est donnée un nom unique, ici bovoyage.

puis, la liaison avec la source de donnée dans l’élément <jta-data-source>.

L’élément racine <persistence> peut avoir plusieurs éléments fils <persistence-unit>. Voici un résumé des éléments principaux pouvant être contenus dans un élément <persistence-unit> :

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

<jta-datasource> : nom JNDI de la source de données transactionnelle ;

<non-jta-datasource> : nom JNDI de la source de données non transactionnelle ;

<mapping-file> : définit un fichier d’association XML si les annotations ne sont pas utilisées dans les entités ;

<properties> : contient des éléments <property> permettant de configurer le fournisseur de persistance ;

<jar-file> : définit les fichiers JAR contenant les entités, par défaut le conteneur analyse l’ensemble des classes de l’archive qui contient le fichier persistence.xml. Ceci peut être utile si les entités sont dans une autre archive.

L’interface javax.persistence.EntityManager permet l’ajout, la modification, la suppression et la recherche des entités. Pour récupérer un EntityManager, il est possible d’utiliser :

les annotations, sous JEE ;

une fabrique EntityManagerFactory, sous JSE.

Les façades de l’exemple utilisent l’EntityManager.

@Stateless @LocalBinding(jndiBinding="ejb3/local/destinationFacade") public class DestinationFacade implements IdestinationFacade @PersistenceContext(name="bovoyage") EntityManager em;

...

L’annotation @PersistenceContext(name="bovoyage") permet au conteneur d’injecter dans la propriété em, l’EntityManager qui a été configurée dans le fichier persistence.xml sous le nom bovoyage.

De nouvelles méthodes callback font leur apparition :

PrePersist invoquée avant la méthode persist(...) de l’Entitymanager ;

PostPersist invoquée après la méthode persist(...) de l’Entitymanager ;

PostLoad invoquée après le chargement ;

PreRemove invoquée avant la méthode remove(...) de l’Entitymanager ;

PostRemove invoquée après la méthode remove(...) de l’Entitymanager ;

PreUpdate invoquée avant une mise à jour en base de données ;

PostUpdate invoquée après une mise à jour en base de données.

Cycle de vie d’un EJB entité

- 10 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

c. Attachement et détachement au contexte de persistance

Un concept important, illustré dans ce schéma du cycle de vie, est le fait qu’une entité peut être attachée ou détachée du gestionnaire d’entités (EntityManager). Pour résumer, tant que l’entité est manipulée par des bean contenus dans le conteneur d’EJB, l’entité est attachée au gestionnaire. Dès que l’entité est manipulée en dehors de ce conteneur, elle est détachée.

Par exemple, lorsqu’une servlet utilise une entité, celle­ci est détachée, pour l’attacher, il faudra utiliser la méthode merge(...) de l’EntityManager.

Dans notre exemple, des instances de Passager sont créées par une action dans le conteneur Web, puis ajoutées à une collection.

public class AjoutPassagersAction implements Action

@Override public String executer(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException

long idSejour =

Long.parseLong(request.getParameter("idSejour"));

String nb = request.getParameter("nb"); if(nb!=null)

int nbPassagers = Integer.parseInt(nb);

List<Passager> passagers = new ArrayList();

try

InitialContext ctx = new InitialContext();

ICommandeFacade cf = (IcommandeFacade)ctx.lookup("ejb3/local/commandeFacade"); for(int i=1 ; i<=nbPassagers ; i++)

String civilite = request.getParameter("civilite_"+i); String nom =

- 11 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

request.getParameter("nom_"+i); String prenom = request.getParameter("prenom_"+i); String email = request.getParameter("email_"+i); String passeport = request.getParameter("passeport_"+i); Passager passager =

new Passager(civilite,nom,prenom,email,passeport);

passager.setIdSejour(idSejour); passagers.add(passager); cf.addPassagers(passagers); catch(Exception e)

System.out.println("Erreurs sur passagers "+e); return "accueil.jsp";

La classe Passager est une entité, mais elle sera persistée par la façade lors de l’appel de la méthode addPassagers(...).

@Stateless @Local(value = IcommandeFacade.class)

@LocalBinding(jndiBinding = "ejb3/local/commandeFacade") public class CommandeFacade implements IcommandeFacade

@PersistenceContext(name = "bovoyage") EntityManager em; @Override public boolean addPassagers(List<Passager> passagers)

boolean ok = true;

try

for (Passager p : passagers)

em.persist(p); catch (Exception e)

System.out.println(e);

ok = false;

return ok;

@Override public boolean addPassager(Passager passager)

boolean ok = true;

try

em.persist(passager); catch (Exception e)

System.out.println(e);

ok = false;

- 12 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

return ok;

Pour gérer les entités, nous utilisons les méthodes de l’EntityManager :

persist(...) permet d’enregistrer une entité ;

merge(...) permet d’attacher une entité ;

remove(...) supprime une entité ;

find(...) récupère une entité par sa clé primaire.

Les modifications de l’état de l’entité sont synchronisées, si l’entité est attachée. Si ce n’est pas le cas, il faut rattacher l’entité avec la méthode merge(...). Par exemple, une servlet récupère une entité contenant une adresse et l’utilisateur modifie l’adresse, l’enregistrement en base ne sera pas modifié car l’entité est détachée.

La recherche des entités peut être effectuée par la méthode find(...), mais aussi par des requêtes.

@Stateless @LocalBinding(jndiBinding="ejb3/local/destinationFacade") public class DestinationFacade implements IdestinationFacade

@PersistenceContext(name="bovoyage") EntityManager em; @Override public List<Destination> getDestinations()

Query query = em.createNamedQuery("findAll"); List<Destination> destinations = query.getResultList(); return destinations;

@Override public Destination getDestination(long id)

Destination destination = em.find(Destination.class, id);

return destination;

@Override public Sejour getSejour(long id)

Sejour sejour = em.find(Sejour.class, id);

return sejour;

Dans cet exemple une requête nommée est utilisée, c’est la requête qui avait été définie dans l’entité :

@Entity @Table(name="destinations") @NamedQueries( @NamedQuery(name="findAll", query="SELECT d FROM Destination as d") ) public class Destination implements Serializable

...

L’utilisation de cette requête passe par l’utilisation d’une instance de Query :

- 13 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

Query query = em.createNamedQuery("findAll"); List<Destination> destinations = query.getResultList();

Le langage EJB­QL 3 est plus puissant que le langage EJB­QL des EJB 2. Mais nous pouvons aussi utiliser des requêtes natives par la méthode createNativeQuery() sur l’EntityManager.

d. Principe du chargement "paresseux"

Les propriétés peuvent être chargées en mode paresseux (lazy loading). Ceci permet d’optimiser la gestion des temps de réponse et la mémoire, si une propriété est une collection d’entités. Dans notre exemple, une instance de Destination contient une collection d’instances de Sejour.

Lors de l’appel de la propriété, celle­ci sera chargée, si l’entité est attachée. En effet, si l’entité est détachée, il se produira une exception. C’est pour cette raison que dans notre exemple, nous avons utilisé un autre mode de chargement pour la propriété sejour de la classe Destination.

@OneToMany(mappedBy="destination", cascade=CascadeType.PERSIST,

fetch=FetchType.EAGER)

private List<Sejour> sejours;

Ceci nous garantit que les séjours seront chargés en même temps que la destination. Attention, ceci peut être dangereux car la charge mémoire peut devenir importante.

4. EJB3 orienté message

Là aussi, la spécification EJB 3 simplifie le codage et le déploiement.

@MessageDriven(name="ReceptionCommande", activationConfig= @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="queue/B"), @ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue="Auto-acknowledge")) public class ReceptionCommande implements MessageListener

@Resource private MessageDrivenContext ctx;

public void onMessage(Message message)

System.out.println("<<< Confirmation de commande a reçu " + message);

Les annotations remplacent les éléments XML des fichiers de configuration.

@MessageDriven annote la classe comme étant orientée message ;

La configuration est une collection d’annotations @ActivationConfigProperty qui reprennent le nom de la propriété et sa valeur.

Cet EJB orienté message est activé par la classe suivante, que nous avons déjà vu dans la présentation des MDB en EJB 2 :

public class ConfirmationSejour

private QueueConnectionFactory factory = null;

private InitialContext ctx = null;

public static final String JNDI_QUEUE = "queue/B";

- 14 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

public static final String JNDI_QUEUE_FACTORY = "QueueConnectionFactory";

private Queue queue = null;

public ConfirmationSejour()

try

ctx = new InitialContext();

factory = (QueueConnectionFactory) ctx.lookup(JNDI_QUEUE_FACTORY);

queue = (Queue) ctx.lookup(JNDI_QUEUE); catch (NamingException e)

System.out.println("ERREUR SUR CONSTRUCTEUR : " + e);

public void envoyerMessage(int numeroSejour)

QueueConnection con = null;

try

con = factory.createQueueConnection(); QueueSession session = con.createQueueSession(false,

Session.AUTO_ACKNOWLEDGE);

QueueSender sender = session.createSender(queue); MapMessage message = session.createMapMessage(); message.setInt("numeroSejour", numeroSejour); message.setString("action", "confirmation"); sender.send(message); System.out.println(">>> MESSAGE ENVOYE : ’" + message + "’"); catch (JMSException jmse)

System.out.println("Erreur lors de l’envoi des messages :

" + jmse.getMessage()); finally

if (con != null)

try

con.close(); catch (JMSException jmse)

public static void main(String[] args)

ConfirmationSejour cl = new ConfirmationSejour();

cl.envoyerMessage(12);

Le principe reste le même : un message est envoyé dans la file d’attente référencée dans le contexte JNDI sous queue/B.

Notre MDB implémente l’interface javax.jms.MessageListener. Lors de la réception du message dans la file, le conteneur peut invoquer la méthode onMessage(...).

- 15 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

Le cycle de vie est toujours aussi simple. Seules les méthodes callback PostConstruct et PreDestroy sont disponibles.

Cycle de vie

- 16 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzS7dYBqWRzIgLAA==-enidentnumber

Utilisation des EJB

Une fois les EJB déployés, ils sont utilisables en local ou à distance selon le type de client. Comme nous avons pu le voir dans le chapitre sur JNDI, la principale difficulté est de retrouver l’objet dans le bon contexte JNDI.

1. Utilisation par une application Web

Si l’application Web est déployée dans le même serveur que le module EJB, le contexte JNDI est le même. Il faut toujours privilégier cette situation car les appels sont alors locaux.

Comme nous avons pu le voir au chapitre Architecture de JBoss, le fichier jboss­web.xml de l’application web permet de lier le nom JNDI global vers un nommage ENC local à l’application :

<jboss-web> <context-root>cwc</context-root> <ejb-ref> <ejb-ref-name>service</ejb-ref-name> <jndi-name>ejb2/service-calculette/remote</jndi-name> </ejb-ref> </jboss-web>

Le fichier web.xml déclare l’EJB et les classes utilisées :

<web-app> ... <ejb-ref> <ejb-ref-name>service</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home> fr.editions.eni.jboss.chap3.ejb2.ServiceCalculetteHome </home> <remote> fr.editions.eni.jboss.chap3.ejb2.ServiceCalculette </remote> <ejb-link>Chap_3_-_EJB#Calculette</ejb-link> </ejb-ref> </web-app>

Ensuite, la servlet peut récupérer l’instance :

InitialContext initCtx = new InitialContext();

Context ctx = (Context)initCtx.lookup("java:comp/env"); ServiceCalculetteHome home = (ServiceCalculetteHome)ctx.lookup(calculetteJNDI);

Il est préférable d’utiliser des noms JNDI dans java:comp/env, plutôt que les noms JNDI globaux, pour des raisons de facilité de maintenance. Si le nom global change, seul le fichier jboss­web.xlm est à maintenir.

L’utilisation des annotations est aussi possible à partir de la version 6 de Tomcat.

2. Utilisation par une application non Web

Pour les clients lourds, le principe de recherche reste le même. Il faut ajouter les bibliothèques JBoss adéquates. En général, la librairie jbossall­client.jar suffit, vous la trouverez dans le répertoire client du répertoire d’installation de JBoss.

Ensuite, vous devez avoir un fichier jndi.properties adapté :

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxObdB80EAksyICwA=-enidentnumber

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

Le code de recherche reste ensuite, le même :

InitialContext ctx = new InitialContext();

ServiceCalculetteHome home =

(ServiceCalculetteHome)ctx.lookup("ejb2/service-calculette/remote");

Le contexte JNDI utilisé ici est évidemment le contexte global, car il est le seul à être accessible en dehors du serveur JBoss.

- 2 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxObdB80EAksyICwA=-enidentnumber

Introduction

Pour illustrer le besoin et le mode de fonctionnement des transactions, nous allons prendre comme exemple, le paiement (simplifié) d’un article sur site marchand.

Les acteurs sont l’acheteur et le fournisseur. Lorsque l’acheteur paie son article, les tâches suivantes sont effectuées :

1. le compte de l’acheteur est débité du montant du prix de l’article ;

2. le compte du fournisseur est crédité du montant du prix de l’article ;

3. la quantité d’articles en stock est mise à jour.

Le diagramme de séquence est le suivant :

Le diagramme de séquence peut être codé de la manière suivante :

... try ... compteAcheteur.debiter(article.getprix()); compteFournisseur.crediter(article.getPrix()); stock.decrementer(article); ... catch(Exception e) // gestion de l’exception ...

Ce code est peu sécurisé, il peut être amené à rencontrer plusieurs problèmes. Lors de la levée d’une exception sur les opérations debiter(...), crediter(...) ou decrementer(...), le code qui suit la levée de l’exception n’est pas exécuté.

Que se passe­t­il si l’opération qui crédite le compte du fournisseur échoue ? Le compte de l’acheteur a déjà été débité, mais le processus ayant été interrompu, il ne recevra jamais sa marchandise.

Nous voudrions donc, lors d’un problème, que toutes les opérations effectuées dans la méthode soient annulées ainsi que les éventuelles modifications en base de données.

Il serait possible de songer à gérer par programmation, les états du processus et d’annuler les opérations si nécessaire. Les bases de données sont, très certainement, sur des serveurs différents, la gestion de stock fait peut­être partie d’une autre application. Des problèmes réseau, de protocole... seront peut­être à gérer. Le développeur va devoir fournir un travail considérable pour gérer les annulations d’opérations, avec le risque important de fragiliser le

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzlyREKKWRzIgLAA==-enidentnumber

code, de le rendre illisible et peu maintenable.

Heureusement, une solution s’offre à nous pour résoudre cette problématique, tout en conservant un code lisible et surtout robuste. Ce sont les transactions.

Le code précédent pourrait devenir :

... try ... transaction.begin(); // début de transaction

compteAcheteur.debiter(article.getprix()); compteFournisseur.crediter(article.getPrix()); stock.decrementer(article); transaction.commit(); // opérations réussies

... catch(Exception e) // gestion de l’exception transaction.rollback(); // annulation des opérations

...

La tâche à effectuer comprend trois opérations :

débiter l’acheteur ;

créditer le fournisseur ;

décrémenter le stock.

Ces trois opérations doivent toutes être menées à bien, ou sinon, aucune d’entre elle ne sera validée. Cette tâche est souvent appelée unité de travail indivisible, ou unité de travail atomique.

La transaction possède des frontières définies :

elle démarre (begin) pour commencer une nouvelle transaction ;

elle peut être validée (commit) ;

elle peut être annulée (rollback).

Une transaction se finit de deux manières : elle est validée (commit) ou annulée (rollback). Si une des opérations qui composent la transaction est invalide, l’ensemble des opérations qui compose la transaction est annulé.

Un acronyme résume les propriétés d’une transaction : ACID :

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzlyREKKWRzIgLAA==-enidentnumber

Atomicité (atomicity) : une transaction doit être atomique, c’est­à­dire qu’elle regroupe des tâches qui composent une unité de travail indivisible.

Cohérence (consistency) : lorsque la transaction est terminée, elle doit laisser le système dans un état cohérent. Par exemple, les bases de données doivent être cohérentes. C’est­à­dire que les règles d’intégrité définies sont valables, qu’aucun enregistrement violant l’intégrité soit ajouté ou retiré lors d’une transaction qui ne se termine pas normalement.

Isolation (isolation) : les différentes transactions doivent être isolées les unes des autres. Pour reprendre notre exemple de site marchand, les transactions liées à chaque acte d’achat doivent être isolées entre elles, bien que les opérations portent sur les mêmes bases de données. Les bases de données gèrent cette isolation, en mettant en place des mécanismes de verrouillage en lecture/écriture des enregistrements et/ou des tables.

Durabilité (durability) : lorsque les changements sont validés, et donc la transaction réussie, les changements doivent être durables, et ceci même en cas de panne serveur, coupure réseau, etc.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzlyREKKWRzIgLAA==-enidentnumber

Utilisation des transactions

Les transactions peuvent être mises en œuvre dans un environnement plus ou moins complexe :

Les transactions locales sont mises en œuvre sur un serveur et sur une ressource.

les transactions distribuées sont mises en œuvre sur un ou plusieurs serveur(s) et une ou plusieurs ressource(s).

Pour reprendre notre exemple, nous pouvons imaginer un bean de type façade encapsulant la transaction, qui effectue les tâches de crédit et débit sur des bases de données différentes.

Sur un serveur d’application, les transactions peuvent être gérées par le serveur (Container Managed Transactions) ou par le développeur au niveau de l’EJB (Bean Managed Transactions).

Nous devons connaître le vocabulaire employé dans les API, et le rôle de chacun des objets.

L’API dédiée aux transactions est JTA, pour Java Transaction API.

Une transaction est contrôlée par le gestionnaire de transaction (TransactionManager).

Des ressources sont impliquées dans les transactions, il s’agit de bases de données ou de files d’attente de messages. L’accès à ces ressources est effectué via un gestionnaire de ressources.

Lorsqu’une transaction est initialisée, un contexte transactionnel lui est associé. Ce contexte transactionnel sera propagé par le gestionnaire de transaction aux différents participants de la transaction.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzk6IZNaWRzIgLAA==-enidentnumber

Modèles des transactions

Plusieurs modèles transactionnels peuvent être mis en œuvre :

gestion des transactions par le bean, BMT pour Bean Management Transaction. Le développeur délimite lui­même les transactions par programmation. Ce modèle ne peut être utilisé que sur des EJB de type session ou message, les délimitations des transactions étant toujours effectuées par le conteneur pour les EJB entité. Le développeur utilise une instance de type javax.transaction.UserTransaction pour délimiter la transaction. Cette instance est disponible auprès du contexte de session du bean.

gestion des transactions par le conteneur, CMT pour Container Management Transaction. C’est une gestion déclarative. L’EJB n’utilise alors aucune méthode de type commit() ou rollback(). Les descriptions et enroulements des EJB dans la transaction sont effectués dans le fichier ejb­jar.xml.

gestion de la transaction par le client. Dans ce cas, c’est le client, une application Web par exemple, qui initie la transaction. Le client récupère un contexte transactionnel auprès de JBoss, via JNDI.

Les exemples suivants sont extraits du projet "Chap 7". Une table (banque), très simple en base de données, est constituée de deux champs :

un champ pour le numéro de compte, clé primaire : compte ;

un champ solde : solde.

Une application Web présentant un formulaire, invoquera, via une servlet un EJB façade qui va permettre de créditer le compte d’un vendeur, et débiter le compte de l’acheteur. La transaction va permettre de gérer le cas où un compte est demandé avec une clé primaire qui n’existe pas, ce qui provoque une exception, et donc ne finit pas le traitement. Comme nous l’avons vu plus haut, un compte peut alors être crédité, sans que l’autre soit débité, si une transaction n’est pas mise en place.

1. Gestion programmée des transactions

L’API JTA (Java Transaction API) permet au développeur de gérer les transactions et leur état. Cette API est utilisable dans les EJB, mais aussi dans les applications clientes, comme les applications Web. Le développeur utilise une instance dérivée de l’interface javax.transaction.UserTransaction pour contrôler les transactions.

Cette interface comporte les méthodes :

void begin() : débute la transaction ;

void commit() : valide une transaction ;

int getStatus() : permet la récupération de l’état de la transaction ;

void rollback() : annule la transaction ;

void setRollbackOnly() : met en place une annulation de transaction ;

void setTransactionTimeout(int) : met à jour le time out de transaction en secondes.

La méthode getStatus() renvoie l’état de la transaction, qui peut être :

STATUS_ACTIVE : transaction en cours et active ;

STATUS_NO_TRANSACTION : pas de transaction en cours ;

STATUS_MARKED_ROLLBACK : transaction marquée pour être annulée ;

STATUS_PREPARING : la transaction est en cours de préparation pour la validation ;

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

STATUS_PREPARED : transaction prête à être validée ;

STATUS_COMMITTING : transaction en cours de validation ;

STATUS_COMMITTED : transaction validée ;

STATUS_ROLLING_BACK : transaction en cours d’annulation ;

STATUS_ROLLEDBACK : transaction annulée ;

STATUS_UNKNOW : transaction en état indéterminé.

a. Gestion programmée des transactions en EJB 2

L’URL pour démarrer l’exemple est : http://localhost:8080/chap7_ejb2/

Le formulaire Web s’affiche alors :

Dans la base de données, seuls les numéros de compte C1 et C2 existent.

L’appui sur le bouton Effectuer le virement appelle la servlet EffectuerVirementServlet, dont le code de la méthode doGet(...) se résume à :

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

récupérer les données du formulaire ;

récupérer la façade et invoquer la méthode qui effectue le virement ;

puis demander l’affichage d’une JSP présentant le résultat.

String cptVendeur = request.getParameter("vendeur"); String cptAcheteur = request.getParameter("acheteur"); double montant = Double.parseDouble(request.getParameter("montant"));

String message = "Virement reussi"; try

BanqueFacadeHome bfh = BanqueFacadeUtil.getHome();

BanqueFacade bf = bfh.create(); boolean ok = bf.effectuerVirement(cptVendeur, cptAcheteur, montant);

if(!ok)

message="Le virement a echoue"; catch(Exception e)

message = "Exception : "+e; RequestDispatcher rd = getServletContext().getRequestDispatcher("/resultat.jsp"); request.setAttribute("message", message); rd.forward(request, response);

L’EJB avec état BanqueFacadeBean gère la transaction de la manière suivante :

public abstract class BanqueFacadeBean implements javax.ejb.SessionBean

private SessionContext ctx;

public boolean effectuerVirement(String compteVendeur, String

compteAcheteur, double montant)

boolean virementEffectue = true;

UserTransaction tm = this.ctx.getUserTransaction();try

tm.begin(); CompteLocalHome home = CompteUtil.getLocalHome(); CompteLocal vendeur = home.findByPrimaryKey(compteVendeur); vendeur.setSolde(vendeur.getSolde()+montant); CompteLocal acheteur = home.findByPrimaryKey(compteAcheteur); acheteur.setSolde(acheteur.getSolde()-montant); tm.commit(); catch(Exception e)

try

tm.rollback(); catch (Exception e1)

// TODO Auto-generated catch block

e1.printStackTrace(); virementEffectue = false;

System.out.println(">>> Erreur sur virement : "+e);

return virementEffectue;

public void setSessionContext(SessionContext arg0)

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

throws EJBException, RemoteException

ctx = arg0;

la méthode setSessionContext(...) met à jour la propriété ctx de type SessionContext.

lors de l’appel de la méthode effectuerVirement :

une instance tm de UserTransaction est récupérée auprès du contexte de session ;

la délimitation de la transaction commence par un tm.begin() ;

si tout se passe bien, la transaction finit avec un tm.commit() ;

si une exception est levée, la transaction se finit par un tm.rollback(), et les éventuelles modifications sont annulées.

Le descripteur de déploiement ejb-jar.xml déclare l’EJB de la manière suivante :

<session id="Session_BanqueFacade"> <display-name>BanqueFacade</display-name> <ejb-name>BanqueFacade</ejb-name> <home>fr.eni.editions.chap7.ejb2.BanqueFacadeHome</home> <remote>fr.eni.editions.chap7.ejb2.BanqueFacade</remote> <local-home> fr.eni.editions.chap7.ejb2.BanqueFacadeLocalHome </local-home> <local>fr.eni.editions.chap7.ejb2.BanqueFacadeLocal</local> <ejb-class> fr.eni.editions.chap7.ejb2.BanqueFacadeSession </ejb-class> <session-type>Stateful</session-type> <transaction-type>Bean</transaction-type> </session>

Nous y retrouvons les éléments qui nous sont, maintenant, familiers. Remarquez la valeur de l’élément <transaction-type> qui est égal à Bean. Ceci indique que la transaction n’est pas gérée par le conteneur.

b. Gestion programmée des transactions en EJB 3

L’exemple ci­dessous est trivial et ne sert qu’à illustrer les transactions programmées en EJB 3.

@Stateless @Local(value = BanqueFacade.class )

@LocalBinding(jndiBinding = "ejb3/local/BanqueFacade") @TransactionManagement(TransactionManagementType.BEAN)public class BanqueFacadeBean implements BanqueFacade

@Resource

UserTransaction ut;

@Resource(mappedName = "java:/jdbc/BovoyageDS")

DataSource ds; public boolean effectuerVirement(String compteVendeur, String

compteAcheteur, double montant)

boolean ok = true;

Connection con = null;

try

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

con = ds.getConnection(); String sqlAcheteur = "UPDATE banque SET solde=solde-? WHERE compte=?"; String sqlVendeur = "UPDATE banque SET solde=solde+? WHERE compte=?"; ut.begin();

PreparedStatement ps = con.prepareStatement(sqlAcheteur); ps.setDouble(1, montant); ps.setString(2, compteAcheteur); if (ps.executeUpdate() != 1)

ok = false;

ps.close(); if (ok)

ps = con.prepareStatement(sqlVendeur); ps.setDouble(1, montant); ps.setString(2, compteVendeur); if (ps.executeUpdate() != 1)

ok = false;

ps.close(); if(ok)

ut.commit();else

ut.rollback();

catch (Exception e)

try

ut.rollback();

catch (Exception e1)

ok = false;

finally

try

con.close(); catch (SQLException e)

return ok;

Le principe de gestion est le même que pour les EJB 2, mais l’accès aux ressources est grandement simplifié par les annotations.

L’annotation @TransactionManagement précise que la transaction est gérée par l’EJB, en prenant la valeur TransactionManagementType.BEAN.

Le contexte de transaction est directement injecté par le contenu lors de l’accès à l’EJB, grâce à l’annotation @Resource sur la propriété d’instance de type UserTransaction.

L’exemple montre une utilisation directe de JDBC, ce qui n’est généralement jamais fait, pour bien mettre en exergue le fait que les commit() et rollback() sont effectués sur l’instance de UserTransaction et non pas sur

l’instance de java.sql.Connection.

2. Gestion déclarative des transactions

Un EJB, dont les transactions sont gérées par le conteneur, ne doit pas utiliser l’interface UserTransaction. L’issue de la transaction est prise en charge par le conteneur.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

Si la sortie de la méthode est effectuée avec une exception système RemoteException alors la transaction est annulée. Attention, la sortie de la méthode par une exception autre qu’une exception système, n’annulera pas la transaction. Elle sera validée.

Si la sortie de la méthode est normale, sans exception système, alors le conteneur vérifie si un EJB a demandé une annulation. Si ce n’est pas le cas, la transaction est validée. Un EJB peut demander une annulation de transaction en invoquant la méthode setRollBackOnly() sur l’objet de type :

EntityContext pour un EJB entité,

SessionContext pour un EJB de type session,

MessageDrivenContext pour un EJB orienté message.

a. Gestion déclarative des transactions en EJB 2

La même URL est utilisée pour afficher le formulaire de test : http://localhost:8080/chap7_ejb2/.

La servlet a été modifiée pour prendre en compte la nouvelle façade BanqueFacadeCMTBean dont les transactions sont gérées par le conteneur.

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException

String cptVendeur = request.getParameter("vendeur"); String cptAcheteur = request.getParameter("acheteur"); double montant = Double.parseDouble(request.getParameter("montant"));

String message = "Virement reussi"; try

BanqueFacadeCMTHome bfh =

BanqueFacadeCMTUtil.getHome();

BanqueFacadeCMT bf = bfh.create(); boolean ok =

bf.effectuerVirement(cptVendeur, cptAcheteur, montant); if(!ok)

message="Le virement a echoue"; catch(Exception e)

message = "Exception : "+e; RequestDispatcher rd = getServletContext().getRequestDispatcher("/resultat.jsp"); request.setAttribute("message", message); rd.forward(request, response);

La méthode effectuerVirement() de l’EJB BanqueFacadeCMTBean a été simplifiée et renvoie une RemoteException en cas d’exception levée, lors de la recherche des EJB entités.

public boolean effectuerVirement(String compteVendeur, String

compteAcheteur, double montant)

throws RemoteException

boolean virementEffectue = true;

try

CompteLocalHome home = CompteUtil.getLocalHome();

CompteLocal vendeur = home.findByPrimaryKey(compteVendeur); vendeur.setSolde(vendeur.getSolde()+montant); CompteLocal acheteur =

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

home.findByPrimaryKey(compteAcheteur); acheteur.setSolde(acheteur.getSolde()-montant); catch(Exception e)

virementEffectue = false;

System.out.println(">>> Erreur sur virement : "+e); throw new RemoteException();

return virementEffectue;

C’est la levée de cette exception qui permettra au conteneur de mettre en place une annulation de la transaction.

Le comportement des EJB vis­à­vis des transactions est décrit dans le descripteur de déploiement ejb­jar.xml :

<container-transaction id="MethodTransaction_1"> <method id="MethodElement_1"> <ejb-name>Compte</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction id="MethodTransaction_2"> <method id="MethodElement_2"> <ejb-name>BanqueFacadeCMT</ejb-name> <method-name>*</method-name> </method> <trans-attribute>RequiresNew</trans-attribute> </container-transaction>

L’élément <method> de l’élément <container-transaction> contient :

<ejb-name> : nom de l’EJB, tel qu’il est défini dans le descripteur ejb­jar.xml.

<method-name> : nom de la méthode, ou nom générique, sur laquelle porte la transaction. Ici, toutes les méthodes des EJB Compte et BanqueFacadeCMT sont prises en compte dans cette définition de la transaction.

La gestion de la transaction est définie dans l’élément <method-attribute> de l’élément <container-transaction>. Les valeurs peuvent être :

NotSupported : la méthode n’est pas enrôlée dans une transaction. Si une transaction existe, elle est suspendue durant l’exécution de la méthode. Le conteneur enrôle la méthode dans un contexte transactionnel non spécifié.

Required : la méthode est toujours invoquée avec un contexte transactionnel. Si la transaction existe lors de l’appel de la méthode, la méthode rejoint la transaction, sinon le conteneur en crée une.

Supports : si une transaction existe lors de l’appel de la méthode alors la méthode est enrôlée dans cette transaction comme Required. Si la transaction n’existe pas, la méthode est appelée dans un contexte transactionnel non spécifié, comme NotSupported.

RequiresNew : la méthode est toujours enrôlée dans une nouvelle transaction. Si une transaction existe, celle­ci est suspendue durant l’exécution de la méthode, pour être reprise ensuite.

Mandatory : la transaction doit exister lors de l’appel de la méthode. Si la transaction n’existe pas, une exception de type javax.ejb.TransactionRequiredException, ou javax.ejb.TransactionRequiredLocalException, est levée.

Never : la méthode ne supporte pas les transactions. Une exception de type javax.rmi.RemoteException est levée si un contexte transactionnel existe.

Si nous avons un objet de type A qui possède une méthode a(), et un objet de type B possédant une méthode b(), les enrôlements en fonction des valeurs Required, RequiresNew, Supports peuvent être résumés de la façon suivante :

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

Attribut de a() Attribut de b() Contexte transactionnel

Client A B

Required Required aucun T1 T1

T1 T1 T1

RequiresNew aucun T1 T2

T1 T1 T2

Supports aucun T1 T1

T1 T1 T1

RequiresNew Required aucun T1 T1

T1 T2 T2

RequiresNew aucun T1 T2

T1 T2 T3

Supports aucun T1 T1

T1 T2 T2

Supports Required aucun aucun T1

T1 T1 T1

RequiresNew aucun aucun T1

T1 T1 T2

Supports aucun aucun aucun

T1 T1 T1

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

b. Gestion déclarative des transactions en EJB 3

Ici aussi, nous pouvons voir la facilité d’utilisation des EJB 3. Il suffit d’annoter la méthode transactionnelle avec l’annotation @TransactionAttribute qui pourra prendre les valeurs :

MANDATORY

NEVER

NOT_SUPPORTED

REQUIRED

REQUIRES_NEW

SUPPORTS

@Stateless @Local(value = BanqueFacadeCMT.class )

@LocalBinding(jndiBinding = "ejb3/local/BanqueFacadeCMT") @TransactionManagement(TransactionManagementType.CONTAINER)

public class BanqueFacadeBeanCMT implements BanqueFacadeCMT

@Resource(mappedName = "java:/jdbc/BovoyageDS") DataSource ds; @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)

public boolean effectuerVirement(String compteVendeur, String

compteAcheteur, double montant)

throws RemoteException

boolean ok = true;

Connection con = null;

try

con = ds.getConnection(); String sqlAcheteur = "UPDATE banque SET solde=solde-? WHERE compte=?"; String sqlVendeur = "UPDATE banque SET solde=solde+? WHERE compte=?"; PreparedStatement ps1 = con.prepareStatement(sqlAcheteur); PreparedStatement ps2 = con.prepareStatement(sqlVendeur); ps1.setDouble(1, montant); ps1.setString(2, compteAcheteur); ps2.setDouble(1, montant); ps2.setString(2, compteVendeur); if (ps1.executeUpdate() != 1)

throw new RemoteException();

if(ps2.executeUpdate()!=1)

throw new RemoteException();

catch (Exception e)

ok=false;

throw new RemoteException();

finally

try

con.close();

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

catch (SQLException e)

return ok;

Si aucun type de transaction n’est défini, alors c’est le type REQUIRED qui est appliqué par défaut. Si l’annotation @TransactionAttribute est positionnée au niveau de la classe, alors, toutes les méthodes non marquées seront gérées par la valeur de l’annotation de classe.

3. Gestion des transactions initiées par le client

Le contexte global JNDI de JBoss possède une entrée UserTransaction. Les applications clientes désirant initier une transaction, doivent utiliser l’interface retournée par la recherche JNDI. Les applications Web déployées dans JBoss possèdent un lien local, par java:/comp/UserTransaction, qui doit être utilisé pour respecter les spécifications ENC.

java:comp namespace of the Chap 7 - Web EJB2.war application: +- UserTransaction[link -> UserTransaction] (class: javax.naming.LinkRef) +- env (class: org.jnp.interfaces.NamingContext) | +- security (class: org.jnp.interfaces.NamingContext) | | +- realmMapping[link -> java:/jaas/other] (class: javax.naming. LinkRef) | | +- subject[link -> java:/jaas/other/subject] (class: javax.naming. LinkRef) | | +- securityMgr[link -> java:/jaas/other] (class: javax.naming. LinkRef) | | +- security-domain[link -> java:/jaas/other] (class: javax.naming. LinkRef)

Le code du client pourrait ressembler à cela :

public boolean effectuerVirement(String cptVendeur, String

cptAcheteur, double montant)

boolean ok = false;

UserTransaction tm=null;

try

InitialContext ctx = new InitialContext();

tm = (UserTransaction)ctx.lookup("java:/comp/UserTransaction"); tm.begin();

BanqueFacadeCMTHome bfh = BanqueFacadeCMTUtil.getHome();

BanqueFacadeCMT bf = bfh.create(); ok = bf.effectuerVirement(cptVendeur, cptAcheteur, montant); tm.commit();

catch (Exception e)

try

tm.rollback();

catch (Exception e1)

// TODO Auto-generated catch block

e1.printStackTrace(); e.printStackTrace(); return ok;

- 10 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzo1DoRqWRzIgLAA==-enidentnumber

Introduction

La plupart des applications nécessitent d’être tout ou en partie sécurisées. La sécurisation prend en compte les concepts suivants :

authentification : l’authentification permet grâce à un identifiant et un mot de passe de vérifier l’identité d’un utilisateur ;

autorisation : ce n’est pas parce qu’un utilisateur est authentifié qu’il a accès à toutes les ressources et traitements. Il faut donc vérifier les autorisations d’accès selon des profils. Ainsi, l’administrateur d’un système n’accède pas aux mêmes ressources et ne possède pas les mêmes droits que l’utilisateur qui un profil "comptabilité" ;

confidentialité : la confidentialité commence avec une restriction des accès aux systèmes d’informations : pas de liaisons Wi­Fi accessibles, ouvertes et non cryptées, par exemple. Ceci va beaucoup plus loin que juste le système d’information car, il met aussi en jeu les individus, avec les risques d’espionnage social ; c’est­à­dire la collecte d’informations auprès des personnes directement lors de conversations par exemple.

intégrité : qui doit prévenir les modifications des informations. Ces modifications peuvent être accidentelles : mauvaise gestion des transactions par exemple. Elles peuvent être causées par une malveillance : l’information est sciemment modifiée.

Les spécifications JEE définissent un framework JAAS (Java Authentification and Authorization Service) de sécurisation qui permet de travailler avec une sécurisation de type déclarative et/ou programmée.

La sécurisation déclarative s’effectue à l’aide de contraintes de sécurité contenues dans les fichiers de configuration des applications Web, ou des EJB. Ceci permet d’avoir une nette indépendance entre le code et la sécurisation.

La sécurisation programmée permet à la logique applicative de gérer la sécurisation, via l’API du framework. Cette approche est beaucoup plus flexible, mais plus coûteuse en implémentation.

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxObdAXXwAksyICwA=-enidentnumber

Introduction aux techniques de chiffrement

Pas de panique, cette introduction n’est présente que pour comprendre les chiffrements qui sont mis en œuvre couramment dans la sécurisation des applications. Il s’agit juste de présenter les grands principes, sans entrer dans les détails mathématiques et algorithmiques.

Une des premières étapes de la sécurisation des applications distribuées, est de sécuriser les communications entre les clients et les serveurs. Pour ce faire, les messages échangés vont être codés. Le fait de coder les messages s’appelle le chiffrement, l’opération inverse est le déchiffrement.

L’opération de chiffrement est effectuée à l’aide d’une clé de chiffrement, le décodage l’est par une clé de déchiffrement.

Lorsque les clés de chiffrement et de déchiffrement sont identiques, les clés sont dites symétriques. Lorsque les clés de chiffrement et de déchiffrement sont différentes, les clés sont dites asymétriques. Le chiffrement est dit aussi, par clé publique.

1. Chiffrement symétrique

La même clé est utilisée pour les opérations de chiffrement et déchiffrement. La clé est connue par les deux parties prenantes de la communication. Ce chiffrement repose sur le principe que les deux utilisateurs (au sens large : humain ou système) connaissent la clé de chiffrement. Il y a donc échange d’un secret : la clé.

Le principal avantage de ce type de chiffrement est sa simplicité, et donc, sa rapidité, en terme de temps de calcul, pour l’algorithme de codage et décodage.

Les désavantages de ce chiffrement symétrique sont les suivants :

Le système repose sur l’échange d’un secret (la clé de chiffrement). L’émetteur du message a besoin de la clé du destinataire. Il faut donc sécuriser le canal d’échange.

Si un utilisateur doit communiquer avec plusieurs autres utilisateurs, il faut une clé par interlocuteur, ce qui, pour un groupe de N utilisateurs désirant utiliser entre eux ce système, il faut distribuer, et gérer N*(N­1)/2 clés.

De plus, il a été démontré par Claude SHANNON, que pour que le système soit totalement sûr, la longueur de la clé doit être égale à la longueur du message.

2. Chiffrement asymétrique

Dans ce type de chiffrement, les opérations de chiffrement et de déchiffrement sont réalisées au moyen de clés différentes.

Le chiffrement est réalisé à l’aide d’une clé dite publique, car connue par tous les émetteurs de message.

Le déchiffrement est réalisé à l’aide d’une clé privée, connue seulement du destinataire des messages.

Contrairement au chiffrement symétrique, les algorithmes de chiffrement et déchiffrement sont totalement différents.

Ce système utilise des fonctions faciles à calculer dans un sens, le chiffrement, et très difficiles à inverser algorithmiquement dans l’autre sens, le déchiffrement. Schématiquement, ces fonctions se comportent comme le carré

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz039LY6WRzIgLAA==-enidentnumber

et son inverse. L’algorithme de la mise au carré d’un nombre est aisé à coder, c’est ce nombre multiplié par lui­même. L’opération inverse, la racine carrée, possède un algorithme déjà nettement plus complexe.

Le destinataire du message crée, aléatoirement, la clé privée. La clé publique est déduite de la clé privée. La clé publique est diffusée par un canal qui n’a pas besoin d’être sécurisé.

Les principaux avantages du chiffrement asymétrique sont les suivants :

il n’y a pas partage d’un secret entre l’émetteur et le destinataire du message, donc pas de nécessité d’utiliser un canal de communication sécurisé ;

il n’y a plus de multiples clés à gérer avec les différents utilisateurs qui veulent échanger des messages. Chaque utilisateur possède une clé privée, et diffuse sa clé publique.

Le principal inconvénient est que le temps de calcul de décodage est moins rapide que pour le chiffrement symétrique.

Il faut aussi considérer que l’émetteur du message doit être sûr que la clé publique qu’il reçoit est bien celle du destinataire. Pour permettre d’assurer l’origine de la clé publique, cette clé va être associée à une entité (organisme, personne, machine…) au sein d’un certificat, qui sera délivré par une autorité de certification (CA pour Certification Authority).

3. Le certificat

L’objectif du certificat est de garantir que la clé publique est bien celle de celui à qui nous voulons envoyer le message. Les certificats sont structurés en deux parties, selon la norme X 509.

Une partie contient les informations sur le certificat lui­même :

Version de la norme X 509 ;

Numéro de série du certificat ;

Algorithme de chiffrement employé ;

Le nom de l’autorité de certification ;

La date de début de validité du certificat ;

La date de fin de validité du certificat ;

L’objet de l’utilisation de la clé publique ;

La clé publique du propriétaire du certificat.

L’autre partie contient la signature de l’organisme de certification. Une empreinte des informations précédentes est créée par une fonction de hashage. Cette empreinte est chiffrée par la clé privée de l’autorité de certification pour générer la signature.

Le certificat est un fichier ressemblant à ceci :

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz039LY6WRzIgLAA==-enidentnumber

Certificate: Data: Version: 3 (0x2) Serial Number: 1218035244 (0x4899be2c) Signature Algorithm: sha1WithRSAEncryption Issuer: C=FR, ST=Paris, L=Paris, O=organisation, OU=unite, CN=client Validity Not Before: Aug 6 15:07:24 2008 GMT Not After : Nov 4 15:07:24 2008 GMT Subject: C=FR, ST=Paris, L=Paris, O=organisation, OU=unite, CN=client Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:94:cb:b5:8a:51:44:a2:97:bd:0d:51:32:dc:0b: 97:7e:15:52:d7:28:41:36:fe:27:cd:83:af:da:3b: 6c:0f:74:9d:68:a2:a1:68:49:f8:22:b0:2e:11:aa: d3:c0:53:7e:26:67:b1:55:da:74:11:39:43:12:df: 0b:f1:31:07:8b:1b:e9:e5:c3:d4:66:bd:7e:20:85: 2b:27:52:48:b7:eb:82:d1:2f:f2:c3:46:2c:c7:5e: e3:34:1d:e9:a2:ba:13:8f:70:6a:d5:ec:f4:2f:a9: a8:2b:38:fb:75:4c:a5:14:a2:d6:f9:45:c9:e8:21: be:b3:d8:42:86:db:c0:b7:1b Exponent: 65537 (0x10001) Signature Algorithm: sha1WithRSAEncryption 63:81:15:4e:7f:09:b0:f0:18:65:a2:99:b8:f0:6e:3a:b8:84: 41:28:98:72:10:93:ce:35:f7:3b:16:fe:61:23:30:b8:b0:67: fd:23:9a:8d:b3:dd:7d:2c:78:69:4c:db:9c:a8:70:a3:47:5f: de:b9:fa:36:5f:ac:4a:34:69:8d:e4:58:a1:a8:30:f9:d6:29: 48:8b:5a:79:18:77:7a:8b:6a:3f:7e:ef:91:63:0c:1d:c6:6f: 83:06:0e:71:06:28:72:f3:b2:4a:52:36:70:3c:98:16:3b:af: 2a:45:30:a4:90:04:1c:87:1c:0f:19:5d:41:33:58:d0:18:b0: 24:24 -----BEGIN CERTIFICATE----- MIICQTCCAaqgAwIBAgIESJm+LDANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJG UjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRUwEwYDVQQKEwxvcmdh bmlzYXRpb24xDjAMBgNVBAsTBXVuaXRlMQ8wDQYDVQQDEwZjbGllbnQwHhcNMDgw ODA2MTUwNzI0WhcNMDgxMTA0MTUwNzI0WjBlMQswCQYDVQQGEwJGUjEOMAwGA1UE CBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRUwEwYDVQQKEwxvcmdhbmlzYXRpb24x DjAMBgNVBAsTBXVuaXRlMQ8wDQYDVQQDEwZjbGllbnQwgZ8wDQYJKoZIhvcNAQEB BQADgY0AMIGJAoGBAJTLtYpRRKKXvQ1RMtwLl34VUtcoQTb+J82Dr9o7bA90nWii oWhJ+CKwLhGq08BTfiZnsVXadBE5QxLfC/ExB4sb6eXD1Ga9fiCFKydSSLfrgtEv 8sNGLMde4zQd6aK6E49watXs9C+pqCs4+3VMpRSi1vlFyeghvrPYQobbwLcbAgMB AAEwDQYJKoZIhvcNAQEFBQADgYEAY4EVTn8JsPAYZaKZuPBuOriEQSiYchCTzjX3 Oxb+YSMwuLBn/SOajbPdfSx4aUzbnKhwo0df3rn6Nl+sSjRpjeRYoagw+dYpSIta eRh3eotqP37vkWMMHcZvgwYOcQYocvOySlI2cDyYFjuvKkUwpJAEHIccDxldQTNY 0BiwJCQ= -----END CERTIFICATE-----

Pour améliorer la manipulation des certificats, ceux­ci sont enregistrés dans des magasins (keystore). Chaque éditeur possède son propre format de keystore, par exemple :

JKS (Java Key Standard) pour le monde Java ;

PCKS12 (Public Key Cryptography Standard 12) pour les navigateurs ;

PEM (Privacy Enchance Mail) pour OpenSSL.

Il nous faudra donc plusieurs outils pour créer et convertir les certificats : keytool, OpenSSL.

4. Notion de clé de session

La clé de session est un compromis entre les chiffrements symétriques et asymétriques. En effet, le chiffrement asymétrique permet de s’affranchir des étapes d’échange de la clé du chiffrement symétrique (le secret partagé), mais ceci au détriment du temps de calcul. La notion de clé de session permet d’utiliser le meilleur des deux techniques.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz039LY6WRzIgLAA==-enidentnumber

a. Principe

L’émetteur du message récupère la clé publique du destinataire.

L’émetteur crée une clé aléatoire qui sera utilisée dans un chiffrage symétrique. Cette clé sera la clé de session. Puis, il chiffre la clé symétrique (la clé de session) qu’il vient de générer, avec la clé publique du destinataire.

L’émetteur envoie au destinataire la clé symétrique chiffrée. Celui­ci déchiffre la clé de session avec sa clé privée. Les deux participants ont maintenant la clé de session en commun, ils peuvent chiffrer et déchiffrer leurs messages avec celle­ci.

En résumé, la clé de session est le secret que partagent les deux interlocuteurs, cette clé sera utilisée pour le chiffrement symétrique. L’envoi de la clé de session se fait par le biais d’un canal sécurisé mis en place par un chiffrement asymétrique.

Le protocole HTTPS dont nous verrons la mise en œuvre plus loin, utilise cette notion de clé de session.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz039LY6WRzIgLAA==-enidentnumber

Introduction à JAAS

Le framework JAAS permet de gérer l’authentification et les autorisations d’un utilisateur. Il crée un objet, partagé par l’authentification et l’autorisation, et étend le modèle de sécurité standard pour gérer cet objet.

Lors du développement d’une application JEE, nous n’avons pas à prendre en compte les profils des utilisateurs et leurs droits vis­à­vis de l’application.

Le modèle déclaratif de sécurité délègue au serveur d’application le soin d’appliquer les contrôles nécessaires. Le développeur peut, aussi, gérer lui­même la sécurité, ceci ne sera pas traité ici.

Chaque utilisateur possède un identifiant et un mot de passe. L’utilisateur est ici défini au sens large, cela peut être une personne, un groupe, un service…

Un utilisateur est relié à un rôle dans l’application. Le rôle représente l’ensemble des droits d’accès à l’application. Le développeur pourra lors du développement, prévoir les rôles qui auront accès à certaines ressources du site Web, ou à certaines méthodes des EJB.

Il faut ensuite définir les mécanismes par lesquels le serveur retrouvera les informations d’identification de l’utilisateur.

1. Les concepts clés de JAAS

Un utilisateur est représenté par sujet, qui est un objet de type Subject. Le sujet est composé d’un ensemble d’identités, de type Principal.

a. Les classes de base de JAAS

javax.security.auth.Subject : représente un demandeur (individu, organisation, groupe) qui peut posséder plusieurs identités. La phase d’authentification va permettre d’authentifier le sujet.

java.security.Principal : représente une identité associée à un objet Subject. Un sujet peut avoir plusieurs identités Principal : son nom (John Doe), son numéro de sécurité sociale (1730844....), son surnom (Joe).

b. Les classes d’authentification des JAAS

javax.security.auth.login.LoginContext : permet aux objets Subject de se connecter et déconnecter du système. Cette API n’est pas liée à la technologie d’identification : écran d’authentification, carte à puce, empreinte…

javax.security.auth.spi.LoginModule : interface qui doit être implémentée par les fournisseurs de service d’authentification. La classe org.jboss.security.auth.spi.UsersRolesLoginModule implémente cette interface. Elle possède 5 méthodes qui permettent l’authentification :

initialize qui initialise l’instance de LoginModule ;

login qui effectue l’authentification ;

commit qui valide l’authentification et ajoute les Principal au sujet ;

abort qui abandonne le processus d’authentification ;

logout qui supprime les Principal.

javax.security.auth.login.Configuration : permet de spécifier quels sont les LoginModule qui seront utilisés, leur ordre d’utilisation et leur comportement.

javax.security.auth.callback.CallbackHandler : interface à implémenter par les applications qui doivent utiliser les informations du service d’authentification, par exemple pour coder la manière dont l’utilisateur saisit son identifiant et mot de passe.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz40Ikb6WRzIgLAA==-enidentnumber

javax.security.auth.callback.Callback : interface marqueur implémentée par les objets utilisés par une implémentation CallbackHandler. Des implémentations standard existent déjà : NameCallback, PaswordCallback par exemple.

c. Exemple d’une implémentation d’authentification personnalisée

Vous trouverez le code complet dans le projet "Chap 8 ­ AAS".

Le code présenté ici est volontairement très simple pour se focaliser sur les mécanismes mis en œuvre. L’objectif est de faire une saisie d’un identifiant et un mot de passe, en mode console, et de valider l’identifiant "toto"’ avec le mot de passe "toto".

La classe MainAuthentification contient le point d’entrée de l’application qui commence par instancier un LoginContext, en lui passant le nom de la configuration et notre classe de type CallbackHandler.

public static void main(String[] args) throws Exception

LoginContext lc = new LoginContext("Exemple",

new AuthCallbackHandler());

lc.login();

Le fichier de configuration exemple_jaas.config contient la configuration requise. Ce fichier est précisé par le passage au compilateur de la propriété :

-Djava.security.auth.login.config==exemple_jaas.config

Si vous utilisez Eclipse, vous devez ajouter cette propriété aux arguments passés à la machine virtuelle, dans la configuration du "run" de votre projet.

Exemple fr.editions.eni.jboss.jaas.ExempleLoginModule required debug=true; ;

On y retrouve :

le nom de la configuration : Exemple ;

la classe qui implémente notre méthode d’authentification et dont les méthodes seront appelées par l’API JAAS, ici : fr.editions.eni.jboss.jaas.ExempleLoginModule ;

flag utilisé si plusieurs LoginModule sont spécifiés. Cela permet à JAAS de définir la stratégie à suivre si une authentification échoue. Les modules LoginModule sont appelés dans l’ordre de leur déclaration ;

required : module nécessaire pour l’authentification globale. Quel que soit le résultat de l’authentification par ce module, l’authentification continue sur les modules suivants ;

requisite : module nécessaire pour l’authentification globale. Si l’authentification est effectuée, les modules suivants sont appelés, sinon le contrôle repasse à l’application ;

sufficient : module non nécessaire pour l’authentification globale. Si l’authentification réussit les autres modules ne sont pas appelés, si elle échoue, les autres modules sont appelés ;

optional : module non nécessaire pour l’authentification globale. Quel que soit le résultat de l’authentification, les modules suivants sont appelés ;

une option qui est liée au module et qui nous permet ici d’afficher des informations si le mode debug est choisi.

L’exemple de configuration suivant, tiré de la documentation de Sun permet de mieux appréhender le rôle du flag

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz40Ikb6WRzIgLAA==-enidentnumber

dans une liste de modules.

Login2 sample.SampleLoginModule required;

com.sun.security.auth.module.NTLoginModule sufficient;

com.foo.SmartCard requisite debug=true;

com.foo.Kerberos optional debug=true;

;

V indique que l’authentification passe.

F indique que l’authentification échoue.

* indique que le module n’est pas appelé.

La classe ExempleLoginModule implémente l’interface LoginModule. Elle est chargée de l’authentification et délègue au CallbackHandler le moyen de récupérer les informations (identifiant et mot de passe) auprès de l’utilisateur.

public boolean login() throws LoginException

// demande l’identifiant et le mot de passe if (callbackHandler == null)

throw new LoginException("ERREUR : pas de CallbackHandler");

Callback[] callbacks = new Callback[2];

callbacks[0] = new NameCallback("identifiant: ");

callbacks[1] = new PasswordCallback("mot de passe: ", false);

try

callbackHandler.handle(callbacks); username = ((NameCallback) callbacks[0]).getName(); pswd = new String(((PasswordCallback)

callbacks[1]).getPassword()); ((PasswordCallback) callbacks[1]).clearPassword(); …

Lors de l’appel de la méthode initialize(..) un Subject et le AuthCallbackHandler ont été passés à l’instance de ExempleLoginModule. La méthode login() crée un tableau de Callback, remplit ce tableau avec un NameCallback et un PasswordCallback, puis la méthode handle(…) de AuthCallbackHandler est invoquée.

La méthode handle(..) de AuthCallbackhandler est chargée de récupérer auprès de l’utilisateur, son identifiant et son mot de passe. Un parcours du tableau des Callback est effectué, et en fonction du type de Callback, le comportement adéquat est mis en place.

… else if (callbacks[i] instanceof NameCallback)

// demande de l’identifiant NameCallback nc = (NameCallback) callbacks[i]; System.err.print(nc.getPrompt());

États d’authentification de Login2

SampleLoginModule required V V V V F F F F

NTLoginModule sufficient V F F F V F F F

SmartCard requisite * V V F * V V F

Kerrebos optional * V F * * V F *

Authentification globale V V V F F F F F

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz40Ikb6WRzIgLAA==-enidentnumber

System.err.flush();

nc.setName((new BufferedReader(new

InputStreamReader(System.in))).readLine()); else if (callbacks[i] instanceof PasswordCallback)

// demande du mot de passe PasswordCallback pc = (PasswordCallback) callbacks[i]; System.err.print(pc.getPrompt());

System.err.flush();

String pswd = new BufferedReader(new

InputStreamReader(System.in)).readLine();

pc.setPassword(pswd.toCharArray());

Les informations sont récupérées sur la ligne de commande et passées aux méthodes des Callback. À l’issue de ce code, NameCallback et PassordCallBack possèdent un identifiant et un mot de passe.

Maintenant, la méthode login() de ExempleLoginModule peut effectuer l’authentification proprement dite.

… if (username.equals("toto"))

usernameCorrect = true;

if (pswd.equals("toto"))

passwordCorrect = true;

Cette méthode renverra true ou false en fonction du résultat.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMz40Ikb6WRzIgLAA==-enidentnumber

Le gestionnaire de sécurité JBossSX

JBossSX, ou JBoss Security Extension, est le composant chargé de la sécurité dans JBoss. JBossSX fournit un modèle de sécurité déclaratif basé sur les rôles.

1. LoginModule de JBoss

Comme nous l’avons vu précédemment, il est toujours possible d’écrire ses propres LoginModule. JBoss nous en fournit déjà quelques­uns.

org.jboss.security.auth.spi.IdentityLoginModule est un module permettant une simple authentification, avec seulement un identifiant et sans mot de passe.

Les options pour ce module sont :

principal : indique que l’identifiant principal sera toujours utilisé ;

roles : précise la liste des rôles qui sont associés au login principal.

org.jboss.security.auth.spi.UsersRolessLoginModule permet une authentification avec identifiant et mot de passe.

Les options disponibles pour ce module sont :

usersProperties : nom du fichier des identifiants et mots de passe ;

rolesProperties : nom du fichier des associations entre identifiants et rôles ;

hashAlgorithm : précise, si nécessaire, le type d’algorithme de chiffrement des mots de passe.

org.jboss.security.auth.spi.LdapLoginModule permet d’utiliser LDAP comme espace de stockage des identifiants et mots de passe.

org.jboss.security.auth.spi.DatabaseServerLoginModule permet d’utiliser une base de données comme espace de stockage des identifiants et mots de passe.

2. Configuration du domaine de sécurité

La configuration des services de sécurité se trouve dans le fichier conf/jboss-service.xml, dont voici un extrait.

<!-- ========================================================== --> <!-- Security --> <!-- ========================================================== --> <mbean code="org.jboss.security.plugins.SecurityConfig" name="jboss.security:service=SecurityConfig"> <attribute name="LoginConfig"> jboss.security:service=XMLLoginConfig </attribute> </mbean> <mbean code="org.jboss.security.auth.login.XMLLoginConfig" name="jboss.security:service=XMLLoginConfig"> <attribute name="ConfigResource"> login-config.xml </attribute> </mbean>

Vous remarquez que le service de sécurité org.jboss.security.plugins.SecurityConfig utilise un service

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSbjSfKWRzIgLAA==-enidentnumber

org.jboss.security.auth.login.XMLLoginConfig qui gère une configuration d’authentification JAAS. Ce fichier login­config.xml est situé dans le répertoire conf.

Ce fichier XML inclut une racine <policy> qui comporte les configurations des domaines de sécurité.

Prenons le domaine de sécurité de la console JMX.

… <application-policy name = "jmx-console"> <authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag = "required"> <module-option name="usersProperties">

props/jmx-console-users.properties </module-option> <module-option name="rolesProperties">

props/jmx-console-roles.properties </module-option> </login-module> </authentication> </application-policy> …

L’élément <login-module> déclare la classe de type LoginModule utilisée, ainsi que le flag de gestion du module.

Puis vient ensuite le paramétrage des options du module dans les éléments <module-option>.

Dans le fichier jmx-console-users.properties se trouve l’affectation des rôles à un utisateur sous la forme :

utilisateur = rôle1, rôle2

Le fichier jmx-console-roles.properties énumère les utilisateurs et mots de passe sous la forme :

utilisateur=mot_de_passe

Pour mieux sécuriser l’application et ne pas avoir la liste des mots de passe en clair dans un fichier, d’autres moyens sont évidemment disponibles : utilisation de base de données, de LDAP…

<application-policy name = "jbossmq"> <authentication> <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule" flag = "required"> <module-option name = "unauthenticatedIdentity">guest</module-option> <module-option name = "dsJndiName">java:/DefaultDS</module-option> <module-option name = "principalsQuery"> SELECT PASSWD FROM JMS_USERS WHERE USERID=? </module-option> <module-option name = "rolesQuery"> SELECT ROLEID, ’Roles’ FROM JMS_ROLES WHERE USERID=? </module-option> </login-module> </authentication> </application-policy>

La classe utilisée ici pour le LoginModule est de type org.jboss.security.auth.spi.DatabaseServerLoginModule. Le nom des tables (JMS_ROLES ici) doit être adapté à votre base de données. Nous retrouvons ensuite les options utilisées par le module :

l’identité attribuée à un utilisateur non authentifié, ici guest ;

le nom JNDI de la source de données utilisée, ici java:/DefaultDS ;

la requête utilisée sur la base, pour retrouver le mot de passe en fonction de l’identifiant entré par l’utilisateur,

Exemple d’authentification avec interrogation d’une base de données

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSbjSfKWRzIgLAA==-enidentnumber

ici SELECT PASSWD FROM JMS_USERS WHERE USERID=?. Le point d’interrogation sera remplacé par l’identifiant lors de l’exécution de la requête ;

la requête utilisée pour récupérer le profil de l’utilisateur en base de données SELECT ROLEID, ’Roles’ FROM JMS_ROLES WHERE USERID=?. Le point d’interrogation sera remplacé par l’identifiant lors de l’exécution de la requête.

Nous allons voir quelques configurations dans la suite de ce chapitre.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzSbjSfKWRzIgLAA==-enidentnumber

Sécurisation d’une application Web

L’objectif de la sécurisation d’une application Web est de donner accès à certaines ressources qu’aux utilisateurs ayant le profil adéquat. Cela suppose d’authentifier l’utilisateur et de vérifier s’il est autorisé à accéder aux ressources demandées.

Le plan de développement de la sécurisation d’une application peut se résumer aux étapes suivantes :

définir la méthode d’authentification ;

définir le domaine de sécurité ;

déclarer les contraintes de sécurité pour les ressources qui doivent être protégées ;

définir les profils d’utilisateurs qui ont le droit d’accéder aux ressources.

Ces étapes ne sont pas liées. Par exemple, nous pouvons changer la méthode d’authentification sans toucher aux autres contraintes.

Différents exemples vont illustrer quelques­unes des configurations possibles. Tous ces exemples sont basés sur la même application Web qui comporte trois espaces de navigation dont certains sont uniquement accessibles à des utilisateurs authentifiés.

1. Mise en place de HTTPS

Les données qui transitent entre le navigateur et le serveur transitent en clair. TSL (Transport Secure Layer), anciennement baptisé SSL (Secure Sockets Layer), permet le chiffrement de ces données. Ce protocole a été développé à l’origine par Netscape, l’IETF (Internet Engineering Task Force) ayant depuis, racheté le brevet.

Le port d’écoute par défaut de HTTPS est le port 443. Ce numéro de port n’a pas besoin d’être précisé dans l’URL. Lorsque l’URL contient le protocole HTTPS, automatiquement le navigateur se connecte sur le port 443.

Ce protocole permet d’atteindre les objectifs suivants :

authentification du serveur : le serveur possède un certificat numérique ;

confidentialité des données échangées entre le navigateur et le serveur ;

intégrité des données échangées ;

possibilité de mettre en place une authentification forte par authentification mutuelle entre le client et le serveur.

Pour mettre en place le protocole HTTPS sur JBoss, il faut :

Installer un certificat numérique qui identifie le serveur et qui contient la clé de chiffrement. Ce certificat est normalement émis par un tiers de confiance, l’autorité de certification (Verisign, par exemple). Nous verrons comment créer notre propre certificat.

Configurer le fichier server.xml du conteneur Web pour ajouter un connecteur sécurisé.

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

a. Création du certificat numérique

Un outil de gestion des certificats : keytool est inclus dans les outils du JDK. Cet utilitaire est dans le répertoire bin du répertoire d’installation du JDK.

Pour créer votre certificat, tapez la commande suivante (sur une seule ligne) :

keytool -keyalg RSA -genkey -alias eni -keystore <répertoire_instal_jboss>/server/default/conf/eni.keystore

ATTENTION : certaines commandes de l’outil keytool ont changé de nom avec JSE 6 :

-export, a été renommée en ­exportcert ;

-genkey, a été renommée en ­genkeypair ;

-import, a été renommée en ­importcert.

Cette commande permet de préciser :

l’algorithme de chiffrement utilisé, par l’option -keyalg, ici RSA ;

l’alias utilisé pour retrouver la clé, par l’option -alias, ici eni ;

le fichier où seront enregistrées les clés, le trousseau de clés, par l’option -keystore, ici, serveur.keystore. Si cette option n’est pas précisée, un fichier .keystore est créé, sans nom devant l’extension (fichier caché sous LINUX).

L’utilitaire vous demande alors le mot de passe qui sera utilisé, ici nous avons entré "editions". Vous devez ensuite confirmer le mot de passe. Ensuite, vous devez entrer un certain nombre de renseignements nom, prénom, ville, etc.

$jboss-4.2.2.GA\server\default\conf>keytool -genkey -keyalg RSA -alias eni -keystore serveur.keystore Tapez le mot de passe du Keystore :editions

Ressaisissez le nouveau mot de passe :editions

Quels sont vos prénom et nom ? [Unknown] : localhost

Quel est le nom de votre unité organisationnelle ? [Unknown] : eni

Quelle est le nom de votre organisation ?

- 2 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

[Unknown] : eni

Quel est le nom de votre ville de résidence ? [Unknown] : Paris

Quel est le nom de votre état ou province ? [Unknown] : France

Quel est le code de pays à deux lettres pour cette unité ? [Unknown] : FR

Est-ce CN=Franck SIMON, OU=editions eni, O=eni, L=Paris, ST=France, C=FR ? [non] : oui

Spécifiez le mot de passe de la clé pour <eni> (appuyez sur Entrée s’il s’agit du mot de passe du Keystore) :

Le premier mot de passe entré correspond au mot de passe du trousseau de clés (le fichier serveur.keystore), le dernier mot de passe correspond à celui de la clé, ici le même que le trousseau de clés. Le fichier keystore a été généré dans le répertoire conf de la configuration de serveur par défaut. Vérifiez que le fichier serveur.keystore soit bien créé.

Notez que les renseignements sur le prénom et nom correspondent à la partie CN (Common Name) du certificat. Certains navigateurs vérifient que la valeur du CN corresponde à une partie du nom de domaine, sur laquelle est faite la requête. S’il n’y a pas correspondance, le navigateur peut alors afficher une message d’avertissement. Ici, nous mettrons donc localhost, dans le monde réel cela pourrait être votre nom de domaine.

Le certificat généré ici est un certificat autosigné. Pour faire signer le certificat par une autorité de certification, des manipulations supplémentaires sont nécessaires, référez­vous à la documentation de l’utilitaire keytool.

b. Configuration du fichier server.xml

Le fichier server.xml se trouve dans le répertoire : <répertoire_instal_jboss>\server\default\deploy\jboss­web.deployer

Avant de manipuler ce fichier, faites­en une copie. Ajoutez l’élément <Connector> suivant, en veillant à le positionner dans la balise <Service>.

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" address="$jboss.bind.address" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="$jboss.server.home.dir/conf/serveur.keystore" keystorePass="secret" />

Les attributs utilisés sont :

port : port d’écoute du connecteur, ici 8443. Il faudra donc préciser le port dans l’URL : https://localhost:8443/secu. Si vous indiquez le port 443, vous n’avez pas besoin de le préciser dans l’URL ;

protocol : il s’agit du protocole HTTP 1.1 ;

SSLEnabled : positionné à true ;

address : adresse d’écoute de JBoss ;

maxThreads : nombre maximum de threads exécutés en même temps sur ce connecteur ;

scheme : préfixe https pour l’URL ;

secure : positionné à true ;

clientAuth : positionné à false pour que le navigateur n’ait pas à envoyer une chaîne certifiée, sauf pour l’authentification en CLIENT­CERT ;

sslProtocol : positionné à TSL ;

keystoreFile : nom du fichier contenant le certificat, avec son chemin ;

- 3 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

keystorePass : mot de passe du certificat ;

keyAlias : nécessaire si plusieurs clés sont présentes dans le trousseau. Il contient alors l’alias vers la clé utilisée.

Redémarrez le serveur, et testez l’URL suivante : https://localhost:8443/.

Par défaut, le port d’écoute en mode HTTPS est le port 443, si le serveur est configuré sur le port HTTPS par défaut, vous n’avez pas à préciser le port, l’URL devient alors : https://localhost/. Attention de bien préciser le protocole https pour activer la communication sécurisée.

Votre navigateur vous avertit alors que vous recevez un certificat. Regardez le contenu du certificat, ici avec Firefox :

Le connecteur sur le port 8080 est toujours actif. L’accès sur le site peut donc aussi être effectué par l’URL : http://localhost:8080/. Si vous souhaitez enlever ce connecteur, mettez­le en commentaire dans le fichier

server.xml. Attention, si vous utilisez Eclipse pour contrôler JBoss, celui­ci utilise le port 8080 pour contrôler le démarrage du serveur.

2. Définition de la méthode d’authentification

Les méthodes d’authentification sont indépendantes du fait de configurer ou non le serveur en HTTPS, sauf pour CLIENT­CERT. Ce chapitre présente, de manière pratique, l’utilisation des méthodes d’authentification. Le lecteur intéressé par une explication complète des protocoles d’authentification HTTP, pourra consulter les spécifications HTTP émises par le consortium W3C.

- 4 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Le conteneur de servlet supporte les méthodes d’authentification suivantes :

Méthode HTTP BASIC : le conteneur envoie un code HTTP 401. Le navigateur affiche une boîte de dialogue modale de saisie de l’identifiant et du mot de passe. Cette boîte de dialogue n’est pas configurable, elle est gérée par le navigateur et peut donc être différente en fonction du navigateur et du système d’exploitation. L’utilisateur saisit, dans cette boîte de dialogue, son identifiant et son mot de passe. Le navigateur envoie l’identifiant et le mot de passe au serveur. L’algorithme de codage des données utilisé est base64, qui est peu sécurisé. Ce mécanisme d’envoi des données est peu sûr, mais il fonctionne quel que soit les navigateurs. Pour sécuriser la transmission des données, l’utilisation de HTTPS est recommandée.

Méthode HTTP DIGEST : comme pour la méthode BASIC, le navigateur va ouvrir une boîte de dialogue modale, pour que l’utilisateur puisse entrer son identifiant et son mot de passe. Les données ne sont pas envoyées en clair : le navigateur calcule une valeur sur 32 caractères hexadécimaux, par hachage (MD5 par défaut) des valeurs fournies par l’utilisateur (son identifiant et son mot de passe). Cette valeur est envoyée au serveur qui pourra la comparer à ses propres données. Ainsi, l’identifiant et le mot de passe ne transitent pas entre le navigateur et le serveur.

Méthode FORM : cette méthode n’est pas basée sur un mécanisme HTTP, ce n’est donc pas le navigateur qui gère la saisie des données d’identification. Le développeur fournit le formulaire de saisie de l’identifiant et du mot de passe, ainsi qu’une page d’erreur d’authentification. Le conteneur web se charge de gérer ces deux pages automatiquement. Si la ressource est protégée, alors le serveur enverra la page d’authentification et gérera les éventuelles erreurs d’authentification. L’envoi des informations saisies par l’utilisateur, identifiant et mot de passe, est effectué par un formulaire, donc non sécurisé, l’utilisation de HTTPS est donc fortement recommandée.

Méthode CLIENT­CERT : elle utilise un certificat qui est normalement émis par une autorité de certification tiers. Nous verrons ici comment émettre notre propre certificat. Cette méthode d’authentification nécessite de travailler avec le protocole HTTPS. Le serveur possède son certificat qui est transmis au navigateur pour HTTPS, et le client possède, lui aussi, son certificat qui est transmis au serveur. Il y a reconnaissance mutuelle des certificats.

3. Définition du domaine de sécurité

Le service de sécurisation offre un mécanisme de protection des ressources. Il gère les accès, les mots de passe et les profils.

4. Déclaration des contraintes

La déclaration des contraintes est effectuée dans le fichier web.xml. Il s’agit de recenser les URLs qui seront protégées et donc, seront soumises à une authentification de l’utilisateur et un contrôle des droits d’accès de l’utilisateur authentifié, pour accéder aux ressources.

5. Présentation de l’exemple de base

Nous avons trois groupes d’utilisateurs :

jacques et line qui ont un profil "admin" ;

gaston et odette qui ont un profil "compta" ;

- 5 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

scarlette et paul qui ont les profils "admin" et "compta".

Les ressources du site Web sont réparties dans trois répertoires :

répertoire admin accessible aux utilisateurs ayant un profil "admin" ;

répertoire compta accessible aux utilisateurs ayant un profil "compta" ;

répertoire public accessible à tout le monde.

Une page d’accueil met en place les liens pour accéder à ces espaces.

L’ensemble de cet exemple est récupérable dans le projet.

a. Mise en place du domaine de sécurité de base

Le domaine de sécurité de base mis en place fait appel à des fichiers textes pour la déclaration des utilisateurs et des profils.

Le fichier des utilisateurs s’appelle exemple­eni­users.properties :

jacques=jacquespw line=linepw gaston=gastonpw odette=odettepw scarlette=scarlettepw paul=paulpw

Le fichier des profils s’appelle exemple­eni­roles.properties :

jacques=admin line=admin gaston=compta odette=compta scarlette=admin,compta paul=admin,compta

Ces deux fichiers sont placés dans le répertoire <répertoire_jboss>/server/default/conf/props. Vous trouverez ces fichiers dans le répertoire ressources du projet "Chap 8 ­ Sécurité Web".

La définition du domaine de sécurité s’effectue dans le fichier <répertoire_jboss>/server/default/conf/login­config.xml. Un élément <application-policy> permet de définir le domaine de sécurité exemple-eni.

<application-policy name = "exemple-eni">

<authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag = "required"> <module-option name="usersProperties">

- 6 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

props/exemple-eni-users.properties </module-option> <module-option name="rolesProperties">

props/exemple-eni-roles.properties </module-option> </login-module> </authentication> </application-policy>

Dans cet élément, nous retrouvons :

le LoginModule utilisé est une classe prédéfinie de JBoss, ici org.jboss.security.auth.spi.UsersRolesLoginModule, avec le flag "required". Ce flag peut prendre les valeurs required, sufficient, requisite ou optional.

Plusieurs balises <login-module> peuvent être présentes, les LoginModule sont alors chaînés, comme expliqué dans la présentation JAAS.

les balises <module-option> sont utilisées par le LoginModule pour son initialisation. L’option rolesProperties contient le nom du fichier des profils et l’option usersProperties, celui des utilisateurs. Les chemins des fichiers sont relatifs aux fichiers login­config.xml.

L’application Web doit prendre en compte le domaine de sécurité. La déclaration du domaine de sécurité est effectuée dans le fichier WEB­INF/jboss­web.xml :

… <jboss-web> <security-domain>java:/jaas/exemple-eni</security-domain>

<context-root>basic</context-root> </jboss-web> …

Le domaine sécurité va ainsi être associé au nom JNDI java:/jaas/exemple-eni.

b. Mise en place de la méthode d’authentification

Le fichier web.xml est modifié afin de prendre en compte la méthode d’authentification.

… <login-config> <auth-method>BASIC</auth-method> <realm-name>Site exemple</realm-name> </login-config> …

Ici, la méthode HTTP BASIC est utilisée, un nom de realm doit être fourni, il apparaîtra sur la boîte de dialogue demandant l’identifiant et le mot de passe de l’utilisateur.

c. Mise en place des contraintes de sécurité

Les contraintes sont aussi ajoutées au fichier web.xml.

La balise <security-constraint> recense les contraintes sur les ressources soumises à des restrictions d’accès.

Les ressources à protéger sont définies dans une balise <web-resource-collection>, qui contient :

les ressources à protéger dans la balise <url-pattern>, ici, les ressources contenues dans le répertoire admin.

les méthodes HTTP, sur lesquelles s’applique la contrainte, dans les balises <http-method>, ici pour GET et POST. Toutes les méthodes HTTP peuvent être listées.

une balise <auth-constraint> qui liste, par l’intermédiaire de balises <role-name>, les profils qui auront le droit d’accéder aux ressources, après authentification par JBoss.

- 7 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

La structure est répétée pour la deuxième contrainte de sécurité qui protège les ressources du répertoire compta.

… <security-constraint> <web-resource-collection> <web-resource-name>Administration</web-resource-name> <description>Exemple de sécurisation de ressources WEB</description> <url-pattern>/admin/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> … </security-constraint> …

d. Déclaration des rôles utilisés

Cette déclaration est effectuée dans le fichier web.xml. Il y a autant de déclarations de rôle que de rôles utilisés dans les contraintes de sécurité.

… <security-role> <role-name>compta</role-name>

</security-role> <security-role> <role-name>admin</role-name>

</security-role> …

Une fois le site déployé, vous pouvez vérifier que les accès aux deux premiers liens sont soumis à une restriction. En fonction de l’utilisateur, vous pouvez avoir accès à compta, admin ou les deux.

Attention, pour effectuer les différents essais, vous devez arrêter et redémarrer votre navigateur, l’identification étant effective pour la session.

- 8 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Et voici ce que le navigateur envoie au serveur. En connaissant base64, il est simple d’en déduire les données envoyées.

GET /secu/admin/admin.jsp HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 Accept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: windows-1250,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost:8080/secu/ Cookie: JSESSIONID=DB1E5FA636A60E8168E5FAFF9378D9EF Authorization: Basic bGluZTpsaW5lcHc=

Dans les paragraphes suivants, nous allons pouvoir tester différentes méthodes d’authentification et types de configuration de domaine de sécurité. Les configurations de domaine de sécurité n’affectent en rien notre application Web. Si le nom du domaine de sécurité change, seul le fichier jboss­web.xml est impacté.

e. Authentification de type DIGEST

L’envoi des informations d’authentification de l’utilisateur vers le serveur possède un niveau de codage plus élevé que dans le cas d’une authentification BASIC. Le processus est différent :

le navigateur fait sa demande de ressource, qui est protégée ;

le serveur lui répond en demandant une authentification de type DIGEST ;

le navigateur utilise les données entrées par l’utilisateur (identifiant et mot de passe) pour calculer, par hachage, la valeur du mot de passe qui sera retournée au serveur ;

le serveur compare la valeur reçue avec celle qu’il connaît. Si les valeurs sont identiques, l’authentification

- 9 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

aboutit.

Côté serveur, il faut :

créer un nouveau fichier de mot de passe avec les valeurs hachées ;

configurer le fichier login­config.xml.

JBoss met à notre disposition une classe qui permet de créer les valeurs de hachage : org.jboss.security.auth.spi.RFC2617Digest. Cette classe est dans le package jbosssx.jar du répertoire lib de la configuration serveur. Les arguments à passer sont :

l’identifiant de l’utilisateur ;

le realm utilisé dans le web.xml pour la méthode DIGEST ;

le mot de passe de l’utilisateur ;

la réponse de l’utilitaire correspond à la valeur hachée, qui devra correspondre à la valeur qui sera transmise par le navigateur.

[lib]$ java -cp jbosssx.jar org.jboss.security.auth.spi.RFC2617Digest toto "Site exemple" titi RFC2617 A1 hash: 5272b2fcc5dd8f8f1b4b3c974dfccfb7

Le fichier contenant les valeurs hachées est, dans notre exemple, sauvegardé sous le répertoire conf de la configuration serveur, sous le nom exemple­eni­users­digest.properties. Vous trouverez ce fichier dans le répertoire ressources du projet Eclispe.

Le fichier créé doit être référencé dans la balise de configuration du login-module.

Les autres options de la balise <login-module> permettent à JBoss d’utiliser la bonne classe avec les options de codage et chiffrement adéquates, afin de renseigner le navigateur lors de la demande d’authentification DIGEST. Si vous souhaitez approfondir ce sujet, reportez­vous aux spécifications du consortium W3C.

<application-policy name="exemple-eni"> <authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required"> <module-option name="usersProperties">props/exemple-eni- users-digest.properties</module-option> <module-option name="rolesProperties">props/exemple-eni- roles.properties</module-option> <module-option name="hashAlgorithm">MD5</module-option> <module-option name="hashEncoding">rfc2617</module-option> <module-option name="hashUserPassword">false</module-option> <module-option name="hashStorePassword">true</module-option> <module-option name="passwordIsA1Hash">true</module-option> <module-option name="storeDigestCallback"> org.jboss.security.auth.spi.RFC2617Digest </module-option> </login-module> </authentication> </application-policy>

Côté application Web, un seul changement est effectué au niveau du fichier web.xml, par rapport au mode d’authentification BASIC.

Création des valeurs hachées

Modification du fichier login­config.xml

Prise en compte de l’authentification DIGEST par l’application Web

- 10 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

… <login-config> <auth-method>DIGEST</auth-method> <realm-name>Site exemple</realm-name> </login-config> …

Selon les navigateurs, l’apparence de la boîte de dialogue peut changer.

Lors de la demande d’authentification, le serveur envoie la réponse suivante au navigateur :

HTTP/1.x 401 Non-Autorisé Server: Apache-Coyote/1.1 Pragma: No-cache Cache-Control: no-cache Expires: Thu, 01 Jan 1970 01:00:00 CET WWW-Authenticate: Digest realm="Site exemple", qop="auth", nonce="2c8166e7b9e7390c6f7cfc17d83f92ce", opaque="3b9f38abf2d02d8c507e5d81d9c4597e" Content-Type: text/html;charset=utf-8 Content-Length: 959 Date: Sun, 06 Apr 2008 08:49:46 GMT

f. Méthode d’authentification de type FORM

Cette authentification ne met pas en œuvre un mécanisme HTTP. Le développeur fournit une page de saisie de l’identifiant et du mot de passe et une page d’erreur d’authentification. Le serveur gère l’envoi de ces pages, automatiquement.

Le fichier de configuration login­config.xml de JBoss correspond à la configuration employée pour la méthode BASIC.

<application-policy name="exemple-eni"> <authentication> <login-module code= "org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required"> <module-option name="usersProperties">props/exemple-eni- users.properties</module-option> <module-option name="rolesProperties">props/exemple-eni- roles.properties</module-option> </login-module> </authentication> </application-policy>

Pour l’application Web, les changements concernent le type d’authentification qui sera défini dans le web.xml, et les pages JSP que le serveur doit envoyer au navigateur lors de la demande d’authentification, et lors d’une erreur

- 11 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

d’authentification.

Le formulaire de saisie des informations d’authentification doit suivre le modèle suivant :

l’action du formulaire doit être : j_security_check ;

le nom du champ de saisie de l’identifiant : j_username ;

le nom du champ de saisie du mot de passe : j_password.

… <form action="j_security_check" method="POST">

<table> <tr><td>Identifiant : </td> <td><input type="text" name="j_username" /></td>

</tr> <tr><td>Mot de passe : </td> <td><input type="password" name="j_password" /></td>

</tr> <tr><td colspan="2"> <input type="submit" value="ENVOYER" /></td></tr> </table> </form>…

La méthode j_security_check est prise en compte par le serveur.

La mise en place de cette authentification est effectuée dans le fichier web.xml.

La méthode d’authentification est FORM.

Les pages JSP envoyées par le serveur sont, dans cet exemple, sous le répertoire auth.

Il n’y a aucun autre impact sur le reste de l’application.

… <login-config> <auth-method>FORM</auth-method>

<form-login-config> <form-login-page>/auth/login.jsp</form-login-page>

<form-error-page>/auth/erreur.jsp</form-error-page>

</form-login-config> </login-config> …

Les pages JSP sont automatiquement gérées par le serveur.

Il faut noter que cette méthode d’authentification n’est pas sécurisée : les informations sont envoyées en clair vers le serveur.

POST /secu/admin/j_security_check HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: windows-1250,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost:8080/secu/admin/admin.jsp Cookie: JSESSIONID=D00D045E62E33501A90627D12D849DB6 Content-Type: application/x-www-form-urlencoded Content-Length: 33 j_username=line&j_password=linepw

Pour sécuriser ce type d’authentification, il est donc conseillé d’utiliser HTTPS.

- 12 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

g. Méthode de sécurisation par certification mutuelle

Cette méthode d’identification nécessite la mise en place de HTTPS pour la liaison avec le serveur et l’envoi par le navigateur, d’un certificat vers le serveur. Il y a donc un échange mutuel de certificat. Le navigateur "connaît’ le serveur et le serveur "connaît" le navigateur.

Il faut, pour mettre en place cette méthode :

créer un certificat pour le serveur, s’il n’existe pas, pour la communication sécurisée ;

créer un certificat pour les clients (les navigateurs) ;

importer les certificats des clients dans un magasin de certificats approuvés (truststore), côté serveur ;

importer dans le navigateur le certificat client ;

configurer JBoss.

Les manipulations peuvent être complexes du fait des différents formats de certificats, et des outils à utiliser.

Pour alléger l’ensemble des opérations qui seront effectuées dans un terminal, nous allons créer un répertoire certificats, dans notre répertoire home sous Ubuntu, ou sous C: pour Windows. Les fichiers créés lors des manipulations suivantes seront mis dans ce répertoire.

Ce certificat est utilisé pour la communication HTTPS, nous allons donc créer celui­ci au format JKS, avec l’utilitaire keytool. Cette étape a été détaillée dans la section HTTPS. Le fichier magasin des certificats s’appellera server.keystore, le mot de passe de ce fichier est : "secret". Le mot de passe n’apparaît pas sur le terminal.

C:\certificats>keytool -genkeypair -alias eni -keystore server.keystore Tapez le mot de passe du Keystore : secret

Ressaisissez le nouveau mot de passe : secret

Quels sont vos prénom et nom ? [Unknown] : localhost

Quel est le nom de votre unité organisationnelle ? [Unknown] : eni

Quelle est le nom de votre organisation ? [Unknown] : eni

Quel est le nom de votre ville de résidence ? [Unknown] : Paris

Quel est le nom de votre état ou province ? [Unknown] : Paris

Quel est le code de pays à deux lettres pour cette unité ? [Unknown] : FR

Est-ce CN=Franck SIMON, OU=eni, O=eni, L=Paris, ST=Paris, C=FR ? [non] : oui

Spécifiez le mot de passe de la clé pour <eni> (appuyez sur Entrée s’il s’agit du mot de passe du Keystore) : C:\certificats>

Nous allons créer maintenant le certificat qui sera envoyé au client. Toujours avec keytool, nous créons le certificat client qui sera mis dans le fichier client.keystore., le mot de passe de ce fichier est "clientpw".

C:\certificats>keytool -genkeypair -alias client -keystore client.keystore Tapez le mot de passe du Keystore : clientpw

Ressaisissez le nouveau mot de passe : clientpw

Quels sont vos prénom et nom ? [Unknown] : client

Quel est le nom de votre unité organisationnelle ? [Unknown] : unite

Quelle est le nom de votre organisation ?

Création du certificat serveur

Création du certificat client

- 13 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

[Unknown] : organisation

Quel est le nom de votre ville de résidence ? [Unknown] : Paris

Quel est le nom de votre état ou province ? [Unknown] : Paris

Quel est le code de pays à deux lettres pour cette unitÚ ? [Unknown] : FR

Est-ce CN=client, OU=unite, O=organisation, L=Paris, ST=Paris, C=FR ? [non] : oui Spécifiez le mot de passe de la clé pour <client> (appuyez sur Entrée s’il s’agit du mot de passe du Keystore) : C:\certificats>

Le serveur a besoin de connaître la liste des clients approuvés qui peuvent se connecter. Les certificats sont sauvegardés dans un magasin des certificats clients, que nous appellerons ici server.truststore.

La première opération est d’exporter le certificat client à l’aide de keytool. La commande de keytool est à saisir sur une seule ligne. Un fichier contenant le certificat sera créé, dans notre cas client.cer.

C:\certificats>keytool -export -alias client -keystore client.keystore -storepass clientpw -file client.cer Certificat enregistré dans le fichier <client.cer>

Il faut maintenant importer le certificat dans le fichier server.truststore. Le mot de passe donné à ce nouveau magasin est "servercert". La commande de keytool est à saisir sur une seule ligne.

C:\certificats>keytool -import -alias client -keystore server.truststore -file client.cer Tapez le mot de passe du Keystore : servercert

Ressaisissez le nouveau mot de passe : servercert

Propriétaireá: CN=client, OU=unite, O=organisation, L=Paris, ST=Paris, C=FR metteurá: CN=client, OU=unite, O=organisation, L=Paris, ST=Paris, C=FR Numéro de sÚrieá: 489c1db1 Valide duá: Fri Aug 08 12:19:29 CEST 2008 auá: Thu Nov 06 11:19:29 CET 2008 Empreintes du certificatá: MD5á: EF:0F:9A:67:3E:D7:36:6E:6C:67:52:95:EF:AA:3A:A8 SHA1á: 23:A3:AC:3C:1C:39:8A:12:F0:94:68:9E:7D:74:F5:BE:21:35:26:98 Nom de lalgorithme de signatureá: 7 Versioná: 8 Faire confiance à ce certificat ? [non] : oui

Certificat ajouté au Keystore C:\certificats>

Avant l’ajout, les caractéristiques du certificat sont affichées, puis la confirmation de l’import vous est demandé.

Vous devez maintenant avoir les fichiers suivants dans le répertoire certificats :

client.cer ;

client.keystore ;

server.keystore ;

server.truststore.

Ici les manipulations sont plus complexes, car keytool n’exporte pas les certificats au format attendu par les navigateurs. Le certificat généré par keytool est au format JKS, celui attendu par les navigateurs doit être au format PKCS12. Il nous faut un autre outil pour faire passer d’un format à l’autre. Nous utiliserons OpenSSL.

OpenSSL est une suite d’outils permettant la gestion du protocole SSL et la gestion de différents types de

Import du certificat client dans le magasin des certificats approuvés

Import du certificat client dans le navigateur

- 14 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

chiffrement. Cette suite logicielle est installée par défaut, sous Ubuntu et de nombreuses autres distributions LINUX. Une installation est nécessaire sous Windows.

Vous trouverez le portage pour Windows à l’adresse : http://www.slproweb.com/products/Win32OpenSSL.html

Choisissez le lien : Win32 OpenSSL v0.9.8h

Sous Windows, l’utilitaire en ligne de commande openssl se trouve dans le répertoire bin du répertoire d’installation.

Nous allons tout d’abord transformer le certificat client en un certificat au format PEM. Puis, nous transformerons le certificat PEM au format PKCS12. Pour cette dernière transformation, nous allons avoir besoin de la clé privée du certificat, et nous devrons l’extraire grâce à une classe ExportPrivateKey écrite en Java, que vous pourrez trouver dans les fichiers téléchargés, ou à l’adresse suivante : http://wiki.jboss.org/wiki/_Files/SSLSetup/client­server­certs.zip

Copiez le source de cette classe, qui se trouve dans le répertoire src de l’archive client­server­certs.zip, dans le répertoire certificats.

/** * @author [email protected] * @version $Revision:$ */ import java.io.File; import java.io.FileInputStream; import java.security.Key; import java.security.KeyPair; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import sun.misc.BASE64Encoder; public class ExportPrivateKey public static void main(String args[]) throws Exception if (args.length < 2) System.err.println("Usage: java ExportPriv <keystore> <alias> <password>"); System.exit(1); ExportPrivateKey myep = new ExportPrivateKey(); myep.doit(args[0], args[1], args[2]); public void doit(String fileName, String aliasName, String pass) throws Exception KeyStore ks = KeyStore.getInstance("JKS"); char[] passPhrase = pass.toCharArray(); BASE64Encoder myB64 = new BASE64Encoder(); File certificateFile = new File(fileName); ks.load(new FileInputStream(certificateFile), passPhrase); KeyPair kp = getPrivateKey(ks, aliasName, passPhrase); PrivateKey privKey = kp.getPrivate(); String b64 = myB64.encode(privKey.getEncoded()); System.out.println("-----BEGIN PRIVATE KEY-----"); System.out.println(b64); System.out.println("-----END PRIVATE KEY-----");

- 15 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

// From http://javaalmanac.com/egs/java.security/GetKeyFromKs.html public KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) throws Exception // Get private key Key key = keystore.getKey(alias, password); if (key instanceof PrivateKey) // Get certificate of public key Certificate cert = keystore.getCertificate(alias); // Get public key PublicKey publicKey = cert.getPublicKey(); // Return a key pair return new KeyPair(publicKey, (PrivateKey) key); return null;

La compilation de cette classe génère des warnings dont vous ne tenez pas compte. Après compilation, nous avons dans le répertoire certificats, les fichiers suivants :

client.cer ;

client.keystore ;

ExportPrivateKey.class ;

ExportPrivateKey.java ;

server.keystore ;

server.truststore.

L’extraction de la clé publique est effectuée par la commande (sur une seule ligne) :

C:\certificats>java ExportPrivateKey client.keystore client clientpw > client.key

La clé publique est maintenant dans le fichier client.key.

Nous pouvons créer la clé au format PKCS12 avec les deux lignes de commandes suivantes :

C:\certificats>openssl x509 -out client.pem -outform pem -text -in client.cer -inform der C:\certificats>openssl pkcs12 -export -out client.p12 -inkey client.key -in client.pem -passout pass:clientpw

Le répertoire certificats contient maintenant le fichier client.p12.

Dans Firefox, l’import du certificat est effectué par le menu Outils ­ Options qui permet l’affichage de la boîte de dialogue suivante :

- 16 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Sur l’onglet Chiffrement, cliquez sur le bouton Afficher les certificats.

Cliquez sur le bouton Importer, sélectionnez le fichier client.p12. Le mot de passe du certificat vous est demandé : clientpw, une fois l’import effectué, la boîte de dialogue affiche le certificat importé.

- 17 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Vous pouvez afficher le contenu du certificat en le sélectionnant et en cliquant sur le bouton Voir.

Dans Internet Explorer (version 7), l’import du certificat s’effectue par le menu Outils ­ Options Internet.

- 18 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Puis cliquez sur le bouton Certificats. Une boîte de dialogue s’ouvre, sélectionnez l’onglet Personnel, puis cliquez sur le bouton Importer.

Un assistant va vous guider dans l’import du certificat. Recherchez le fichier client.p12 à l’aide de l’assistant, attention de bien sélectionner le type de fichier dans la boîte de dialogue d’ouverture de fichier. Puis, entrez le mot de passe (clientpw) et le magasin de stockage du certificat : Personnel. Une fois l’import terminé, la boîte de dialogue affiche le certificat.

- 19 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Vous pouvez étudier les détails du certificat en le sélectionnant puis en cliquant sur Affichage.

À ce stade, vous pouvez copier les fichiers server.keystore et server.truststore du répertoire vers le répertoire conf du serveur par défaut de JBoss. Il nous faut ensuite configurer le fichier server.xml du service web : <repertoire installation JBoss>/servers/default/deploy/jboss­web.deployer/server.xml.

Comme pour la liaison HTTPS, nous allons activer le port 8443, et en plus, nous préciserons l’accès au magasin des certificats clients. Pour ce faire, nous devons modifier et décommenter si nécessaire, le connecteur HTTPS.

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="$jboss.server.home.dir/conf/server.keystore" keystorePass="secret" truststoreFile="$jboss.server.home.dir/conf/server.truststore" truststorePass="servercert" />

Nous y retrouvons les conditions d’accès au magasin de la clé du serveur pour HTTPS et celles du magasin des certificats client pour l’authentification. L’attribut clientAuth est mis à true pour activer cette authentification.

Une fois les modifications effectuées, vous devez relancer JBoss et vérifier qu’il n’y a pas d’erreur dans la console (fichier non trouvé, ou mot de passe erroné).

Vous pouvez maintenant faire un premier essai pour voir si la certification mutuelle fonctionne, saisissez l’adresse : https://localhost:8443/.

Le navigateur peut attirer votre attention sur le fait que les certificats employés ne sont pas sûrs. En effet cela est normal car ils ne sont pas signés par une autorité de confiance. Poursuivez la navigation.

Si le certificat client a été importé sur le navigateur, vous devez visualiser la page d’accueil de JBoss.

Si le navigateur ne possède pas de certificat, vous devez avoir une erreur et la navigation ne peut pas être effectuée.

Exemple de boîte de dialogue d’erreur sur Firefox :

Configuration de JBoss

- 20 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Cette boîte de dialogue peut être différente en fonction des navigateurs et systèmes d’exploitation.

Pour l’instant, les certificats sont installés. Le navigateur et le serveur "se font confiance" via l’échange des certificats. Il faut ensuite que l’utilisateur puisse s’authentifier.

Vous pouvez ajouter l’authentification de type BASIC à votre site sur cette certification mutuelle.

Toutes les méthodes d’authentifications vont permettre de créer un sujet (Principal) qui va pouvoir être transmis d’un conteneur à l’autre.

Vous pouvez, par programmation, connaître le nom de l’utilisateur en interrogeant son Principal.

public class CalculerServlet extends javax.servlet.http.HttpServlet

implements javax.servlet.Servlet

static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException

... Principal user = request.getUserPrincipal();if(user!=null)

System.out.println(">>> USER : "+user.getName());

...

L’objet request permet de récupérer le Principal de l’utilisateur. Si l’utilisateur n’est pas authentifié, l’objet de type java.security.Principal n’existe pas. Vous pouvez voir aussi, sur l’objet request, si l’utilisateur appartient à un profil particulier.

boolean admin = request.isUserInRole("admin");

- 21 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOWWm6cUAksyICwA=-enidentnumber

Accès aux EJB

Une fois l’authentification effectuée, le contexte de sécurité est propagé d’un conteneur à l’autre :

du conteneur client, pour un client lourd, vers le conteneur EJB ;

du conteneur JSP vers le conteneur EJB ;

d’un conteneur EJB vers un autre conteneur EJB.

Au niveau de l’EJB, il est possible de gérer les autorisations d’accès aux méthodes.

Il faut que les différents conteneurs utilisent le même contexte d’autorisation. Cela peut être vérifié en affichant les noms JNDI.

java:comp namespace of the Chap 8 - Sécurité Web.war application:

+- UserTransaction[link -> UserTransaction] (class: javax.naming.LinkRef) +- env (class: org.jnp.interfaces.NamingContext) | +- security (class: org.jnp.interfaces.NamingContext) | | +- realmMapping[link -> java:/jaas/exemple-eni]

(class: javax.naming.LinkRef) | | +- subject[link -> java:/jaas/exemple-eni/subject] (class: javax.naming.LinkRef) | | +- securityMgr[link -> java:/jaas/exemple-eni] (class: javax.naming.LinkRef) | | +- security-domain[link -> java:/jaas/exemple-eni]

(class: javax.naming.LinkRef) Ejb Module: Chap 8 - EJB2.jar java:comp namespace of the Calculette bean:

+- env (class: org.jnp.interfaces.NamingContext) | +- security (class: org.jnp.interfaces.NamingContext) | | +- subject[link -> java:/jaas/exemple-eni/subject] (class: javax.naming.LinkRef) | | +- security-domain[link -> java:/jaas/exemple-eni]

(class: javax.naming.LinkRef)

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzC7JtoKWRzIgLAA==-enidentnumber

java: Namespace

... +- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory) +- jaas (class: javax.naming.Context)

| +- exemple-eni (class: org.jboss.security.plugins.SecurityDomainContext)

| +- other (class: org.jboss.security.plugins.SecurityDomainContext) | +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext) | +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext) | +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)

1. Gestion des autorisations sur les EJB 2

Les extraits de code et de fichiers xml sont issus du projet "Chap 8 ­EJB2". Le projet Web "Chap 8 ­ Sécurité Web" permet de tester l’EJB. Vous devez déployer les deux projets dans votre serveur. L’URL de test est : http://localhost:8080/secu. Pour être authentifié, vous devez d’abord cliquer sur le lien Administration du site, puis choisir EJB2 dans la liste déroulante.

<ejb-jar> <enterprise-beans> <!-- Session Beans --> <session id="Session_Calculette"> <display-name>Calculette</display-name> <ejb-name>Calculette</ejb-name> <home>fr.eni.editions.ejb2.secu.CalculetteHome</home> <remote>fr.eni.editions.ejb2.secu.Calculette</remote> <local-home> fr.eni.editions.ejb2.secu.CalculetteLocalHome </local-home> <local>fr.eni.editions.ejb2.secu.CalculetteLocal</local> <ejb-class> fr.eni.editions.ejb2.secu.CalculetteSession </ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <security-role-ref>

<role-name>admin</role-name>

<role-link>admin</role-link>

</security-role-ref>

</session>

Descripteur de déploiement ejb­jar.xml

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzC7JtoKWRzIgLAA==-enidentnumber

</enterprise-beans> ... <assembly-descriptor id="AssemblyDescriptor_1"> <security-role>

<description>

<![CDATA[Profil administrateur]]>

</description>

<role-name>admin</role-name>

</security-role>

... </ejb-jar>

Ce premier extrait du fichier ejb­jar.xml nous permet de déclarer les références aux rôles de sécurité. L’élément <security-role-ref> contient le mapping entre :

<role-name> qui est le nom du rôle qui est utilisé dans l’EJB, en gestion programmée de l’authentification ;

<role-link> correspond à un des rôles définis dans la balise <security-role>.

L’élément <assembly-descriptor> contient la définition des rôles qui seront utilisés par le composant. Les rôles sont définis dans la balise <security-role> qui contient :

<description> correspond à une description éventuelle du rôle ;

<role-name> correspond à un rôle de sécurité.

Ensuite, une autorisation est mise en place pour les méthodes de l’EJB au sein de l’élément <assembly-descriptor>. Le mapping entre les autorisations de méthodes et les rôles de sécurité est effectué dans la balise <method-permission>.

<ejb-jar> ... <method-permission id="MethodPermission_4"> <description> <![CDATA[description not supported yet by ejbdoclet]]> </description> <unchecked/> <method id="MethodElement_4"> <description> <![CDATA[<!-- begin-xdoclet-definition -->]]> </description> <ejb-name>Calculette</ejb-name> <method-intf>Remote</method-intf> <method-name>additionner</method-name> <method-params> <method-param>int</method-param> <method-param>int</method-param> </method-params> </method> </method-permission> <method-permission id="MethodPermission_6"> <description> <![CDATA[description not supported yet by ejbdoclet]]> </description> <role-name>admin</role-name> <method id="MethodElement_6"> <description><![CDATA[<!-- begin-xdoclet-definition -->]]> </description> <ejb-name>Calculette</ejb-name> <method-intf>Remote</method-intf>

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzC7JtoKWRzIgLAA==-enidentnumber

<method-name>soustraire</method-name> <method-params> <method-param>int</method-param> <method-param>int</method-param> </method-params> </method> </method-permission> ... </ejb-jar>

L’élément <method-permission> comporte les éléments fils :

<description> contient un texte optionnel décrivant le mapping ;

<role-name> rôle de sécurité permis pour utiliser la méthode. Cette balise peut être présente plusieurs fois, ou être absente ;

<unchecked> précise que la méthode n’est pas liée à un rôle particulier ;

<method-name> contient le nom de la méthode. Peut aussi contenir un nom générique type * pour toutes les méthodes de l’EJB, ou get* pour tous les getters ;

<ejb-name> nom de l’EJB ;

<method-inf> distingue le type d’interface pour la méthode : Remote, Home, Local ou LocalHome ;

<method-params> contient la liste des types des paramètres de la méthode, ce qui permet de préciser une signature particulière en cas de surcharge de la méthode.

Le domaine de sécurité a été ajouté au fichier jboss.xml.

<jboss> ... <security-domain>java:/jaas/exemple-eni</security-domain>

<enterprise-beans> <session> <ejb-name>Calculette</ejb-name> <jndi-name>ejb2/remote/Calculette</jndi-name> <local-jndi-name>ejb2/local/Calculette</local-jndi-name> <method-attributes> </method-attributes> </session> </enterprise-beans> ... </jboss>

Dans l’exemple, l’EJB CalculetteBean possède deux méthodes : additionner(...) et soustraire(...). Pour accéder à ces méthodes, il faut que l’utilisateur soit authentifié. La méthode additionner(...) peut être invoquée quel que soit l’utilisateur, tandis que la méthode soustraire(...) n’est utilisable que par un utilisateur ayant un profil "admin". Le conteneur d’EJB lève une exception si les conditions de sécurité ne sont pas respectées lors de l’invocation d’une méthode, y compris sur les méthodes du cycle de vie.

Descripteur de déploiement jboss.xml

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzC7JtoKWRzIgLAA==-enidentnumber

2. Gestion des autorisations sur les EJB 3

Les extraits de code et de fichiers xml sont issus du projet "Chap 8 ­EJB3". Le projet Web "Chap 8 ­ Sécurité Web" permet de tester l’EJB. Vous devez déployer les deux projets dans votre serveur. L’URL de test est : http://localhost:8080/secu. Pour être authentifié, vous devez d’abord cliquer sur le lien Administration du site, puis choisir EJB3 dans la liste déroulante.

... @Local @Stateless @LocalBinding(jndiBinding="ejb3/local/Calculette") @SecurityDomain("exemple-eni") public class CalculetteBean implements Calculette

@Unchecked public int additionner(int a, int b)

return a+b;

@RolesAllowed("admin") public int soustraire(int a, int b)

return a-b;

Nous retrouvons les configurations équivalentes aux fichiers de déploiement des EJB 2, mais exprimées par les annotations. Dans notre exemple le fichier ejb­jar.xml n’est pas présent.

La description de configuration s’en trouve allégée, il n’y a plus besoin, entre autres, de définir les signatures.

Les annotations utilisées ici sont :

@securityDomain : contient le domaine de sécurité. Il s’agit d’une annotation JBoss facultative. Si elle n’est pas présente, le fichier jboss.xml doit déclarer le domaine de sécurité dans l’élément <security-domain>. Attention, la valeur de l’annotation n’est pas le nom JNDI mais uniquement le nom du domaine. Le nom JNDI du domaine est : java:/jaas/exemple-eni, nous utilisons donc uniquement exemple-eni.

@Unchecked : correspond à l’élément <unchecked> du fichier de déploiement ejb­jar.xml. La méthode n’est pas liée à l’authentification sur un rôle particulier.

@RolesAllowed : correspond à l’élément <role-name> du fichier ejb­jar.xml. La valeur de l’annotation contient donc, les rôles autorisés à utiliser la méthode. La valeur de cette annotation est un tableau de String, d’où l’utilisation des accolades pour le passage de la valeur. Si plusieurs rôles sont définis, ils sont dans l’accolade, séparés par des virgules, par exemple @RoleAllowed("admin","gestion").

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzC7JtoKWRzIgLAA==-enidentnumber

Instancier plusieurs serveurs sur une même machine

Il peut être nécessaire, ou pratique, de faire exécuter plusieurs instances de JBoss sur une même machine. Dans le cadre de tests, nous n’avons pas forcément le nombre de machines nécessaires sous la main. Cette solution peut donc s’avérer très pratique, à condition de disposer des ressources système nécessaires en mémoire et puissance processeur.

Des tests de mise en cluster peuvent ainsi être effectués. De même, des test sur des instances JBoss utilisant des versions de JVM différentes peuvent être pratiqués. Dans ce dernier cas, il ne peut pas y avoir de cluster mis en place.

Plusieurs instances de JBoss ne peuvent pas être exécutées sur une même machine sans qu’apparaissent des conflits, car il ne peut pas y avoir deux applications qui écoutent sur une même adresse IP, et sur un même port. Il faut donc changer les ports d’écoute de chaque instance.

Cette opération est facilitée par l’existence d’un service ServiceBinding, normalement non activé dans les configurations serveur. Un fichier de configuration exemple permet de facilement accroître le nombre d’instances.

Pour réaliser cette opération, nous devons :

afficher le fichier de configuration des liaisons entre chaque serveur et ses ports d’écoute ;

créer par copie autant de serveurs que nécessaire ;

modifier le fichier jboss­service.xml de chacun des serveurs.

1. Fichier sample­bindings.xml

Le fichier sample­bindings.xml se trouve dans le répertoire <repertoire installation>docs\examples\binding­manager.

La configuration des ports d’écoute de chaque instance de serveur est décrite dans un élément <server> contenant le nom de la configuration.

<service-bindings> <!-- ********************************************************** --> <!-- * ports-default * --> <!-- ********************************************************** --> <server name="ports-default"> ...

Le fichier fourni avec la distribution JBoss comporte 4 configurations différentes, nommées :

ports-default ;

ports-01 ;

ports-02 ;

ports-03 ;

Les ports d’écoute pour JNDI et Tomcat sont, entre autres, définis dans chacune de ces configurations :

Ainsi, pour la configuration ports-default, le port d’écoute du service de nommage est 1099 :

<service-config name="jboss:service=Naming" delegateClass="org.jboss.services.binding.AttributeMappingDelegate"> <delegate-config portName="Port" hostName="BindAddress"> <attribute name="RmiPort">1098</attribute> </delegate-config> <binding port="1099" host="$jboss.bind.address"/> </service-confi

Et le port d’écoute HTTP est 8080 :

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZ0tksKWRzIgLAA==-enidentnumber

<service-config name="jboss.mq:service=InvocationLayer,type=HTTP" delegateClass="org.jboss.services.binding.AttributeMappingDelegate" > <delegate-config portName="URLPort"/> <binding port="8080"/> </service-config>

Il suffit d’ajouter 1000 au port d’écoute par défaut, pour avoir les ports d’écoute des autres configurations :

2. Copie d’une configuration de base

Nous allons partir de la configuration default pour créer default-01 et default-02.

Pour ce faire, il suffit de copier le répertoire default en le renommant. Vous devez maintenant avoir sous le répertoire server de votre installation JBoss, les configurations serveurs suivantes :

all

default

default-01

default-02

minimal.

3. Changement des configurations serveurs

Nous allons changer les configurations des serveurs default­01 et default­02 pour qu’ils puissent utiliser le service ServiceBinding.

Ouvrez le fichier jboss­service.xml de default­01 et cherchez la section contenant la référence au service ServiceBinding.

<mbean code="org.jboss.services.binding.ServiceBindingManager" name="jboss.system:service=ServiceBindingManager"> <attribute name="ServerName">ports-01</attribute>

<attribute name="StoreURL">$jboss.home.url/docs/examples/binding- manager/sample-bindings.xml</attribute> <attribute name="StoreFactoryClassName"> org.jboss.services.binding.XMLServicesStoreFactory </attribute> </mbean>

Décommentez le bloc déclarant le Mbean, en prenant soin de commenter le texte précédant l’élément déclarant le MBean.

Par défaut le nom du serveur est ports-01, nous allons le laisser à cette valeur pour la configuration default-02. Cette valeur fait référence à la déclaration du serveur dans le fichier binding­manager.xml vu précédemment.

Configuration Service de nommage Écoute HTTP

ports-default 1099 8080

ports-01 1199 8180

ports-02 1299 8280

ports-03 1399 8380

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZ0tksKWRzIgLAA==-enidentnumber

Notez qu’un élément <attribute name="StoreURL"> référencie le fichier de configuration des ports.

Opérez la même manipulation sur le fichier jboss­service.xml de la configuration default-02, en renommant le serveur en ports-02. Vous devriez avoir la configuration suivante pour ce serveur :

<mbean code="org.jboss.services.binding.ServiceBindingManager" name="jboss.system:service=ServiceBindingManager"> <attribute name="ServerName">ports-02</attribute>

<attribute name="StoreURL">$jboss.home.url/docs/examples/binding- manager/sample-bindings.xml</attribute> <attribute name="StoreFactoryClassName"> org.jboss.services.binding.XMLServicesStoreFactory </attribute> </mbean>

4. Démarrage des serveurs

Ceci est devenu classique pour vous, maintenant. Les serveurs sont démarrés dans des consoles différentes, avec les commandes suivantes : run -c default-01 pour la première configuration et run -c default-02 pour la seconde configuration.

Vous ne devez pas avoir d’erreur lors du démarrage des serveurs. Si c’est le cas, il y a des ports communs, vérifiez alors les points précédents.

Si les serveurs ont démarré sans erreur, vous pouvez atteindre les pages d’administration de JBoss par les URLs :

http://localhost:8180/ pour le serveur default-01

http://localhost:8280/ pour le serveur default-02.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzZ0tksKWRzIgLAA==-enidentnumber

Mise en cluster de plusieurs serveurs

La mise en cluster de plusieurs instances de JBoss permet à une application d’être exécutée sur plusieurs serveurs. La charge peut alors être distribuée entre les serveurs, et si un des serveurs ne fonctionne plus, l’application est toujours accessible par l’intermédiaire des autres serveurs.

Un cluster est un ensemble de nœuds, un nœud étant une instance particulière de JBoss. Pour monter un cluster, il suffit donc de grouper plusieurs instances de JBoss, ce groupement est appelé une partition. Plusieurs partitions peuvent exister sur un même réseau, chaque partition ayant un nom unique au sein du réseau.

La configuration serveur allest configurée pour fonctionner en mode serveur. Dans cette partie dédiée à la mise en cluster de JBoss, nous utiliserons cette configuration, donc tous les fichiers et répertoires décrits ci­après seront dans le répertoire du serveur all.

Le nom de la partition est configuré dans le fichier cluster­service.xml du répertoire deploy.

<mbean code="org.jboss.ha.framework.server.ClusterPartition" name="jboss:service=$jboss.partition.name:DefaultPartition"> <!-- Name of the partition being built --> <attribute name= "PartitionName">$jboss.partition.name:DefaultPartition</attribute> ...

Le nom de la partition peut être passé en ligne de commande, lors du démarrage du serveur.

Sous Windows :

run -c all -Djboss.partition.name=partition1

Sous Linux :

./run.sh -c all -Djboss.partition.name=partition1

Les fonctionnalités mises en œuvre par JBoss permettent :

la découverte automatique des nœuds d’une même partition ;

la reprise automatique en cas de panne d’un nœud ;

la répartition de charge pour les services JNDI et RMI ;

- 1 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOckInhUBksyICwA=-enidentnumber

la réplication de l’arborescence JNDI au travers du cluster ;

la distribution des applications sur tous les nœuds du cluster lorsque l’application est déposée dans le répertoire farm ;

l’exécution unique d’une application au travers du cluster par le répertoire deploy­hasingleton.

La configuration all possède deux répertoires supplémentaires :

farm qui est le répertoire de déploiement des applications qui devront être répliquées sur les autres nœuds du cluster. Une application déposée dans ce répertoire va automatiquement être dupliquée dans les répertoires farm des autres nœuds.

deploy­hasingleton qui est le répertoire des applications qui ne doivent pas être dupliquées sur les autres nœuds du cluster. L’application déposée dans ce répertoire sera accessible par les autres nœuds du cluster, sans que l’application soit dupliquée. Attention dans ce cas aux échanges sur le réseau, et à la parte de la fonction de contournement des pannes (fail­over), si le nœud qui contient l’application singleton tombe en panne.

1. La répartition de charge

La répartition de charge (load balancing) consiste à interposer entre le client et le cluster, un répartiteur qui va répartir les demandes des clients vers les nœuds du cluster, en fonction de leur disponibilité et de leur taux d’occupation. Ce répartiteur peut être logiciel ou matériel. Différentes stratégies de répartition de charge peuvent être mises en œuvre. JBoss intègre les stratégies suivantes :

Round­Robin : chaque appel est relayé à un nouveau serveur. Le premier serveur étant sélectionné aléatoirement.

First Available : un des serveurs est élu comme cible principale et utilisé pour tous les appels. Lorsque la liste des serveurs change, un nouveau serveur est élu.

Attention, la répartition de charge ne tient pas forcément compte du suivi des sessions HTTP. Ce qui signifie qu’il faut un mécanisme supplémentaire pour qu’un client HTTP puisse toujours cibler le même nœud, c’est la notion d’affinité de session, qui sera abordée plus loin.

- 2 - © ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOckInhUBksyICwA=-enidentnumber

2. Lancement du cluster

Pour effectuer les tests suivants, nous devons lancer plusieurs serveurs en configuration all. Pour ce faire, vous pouvez :

lancer chaque serveur sur des ordinateurs différents configurés sur un même réseau ;

si vous ne disposez pas de plusieurs ordinateurs vous pouvez lancer plusieurs serveurs sur une machine, comme nous l’avons vu précédemment ;

vous pouvez aussi utiliser des outils de virtualisation, comme Sun xVM VirtualBox.

Vous pouvez suivre l’évolution des nœuds de votre cluster sur la console du serveur qui a été lancé en premier.

Vous pouvez visualiser les différents nœuds du cluster en interrogeant la console JMX d’un des nœuds et en sélectionnant le service JBoss dont le service porte le nom de votre cluster.

Vous pouvez retrouver sur cet écran les adresses IP et les numéros de port d’écoute des différents serveurs du cluster nommé "MonCluster".

3. Déploiement d’application

Le déploiement est assuré par le service JBoss nommé farm. Il fonctionne comme le service de déploiement de base, en gérant le déploiement sur les machines du cluster.

Il suffit de déposer dans le répertoire farm une archive pour qu’elle soit déployée et copiée dans le répertoire farm de chaque nœud du cluster.

Utilisez une des archives que nous avons créées lors des précédents tests et copiez­la dans le répertoire farm d’un des serveurs. Vous pouvez vérifier que cette archive a bien été prise en compte dans la console des différents serveurs, et que l’archive a bien été recopiée automatiquement dans chaque répertoire farm des autres nœuds du cluster.

- 3 -© ENI Editions - All rigths reserved - Bozahi Ali

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA6Mzg3MTg3IC0gQm96YWhpIEFsaSAtIDkyZjZhMGFjLTQyMjMtNDEzYS1iMjQxLWVmODVjNWNlMTUxOckInhUBksyICwA=-enidentnumber

Service HTTP

La réplication des sessions permet de répliquer les états des sessions des clients entre les différents nœuds du cluster. Cette tâche est effectuée par JBoss lorsque le serveur est démarré dans la configuration all.

Par contre, la répartition de charge n’est pas prise en compte par JBoss et nécessite un logiciel, ou matériel supplémentaire.

Le répartiteur de charge trace les requêtes et, en fonction de la session entre le navigateur et le serveur, transmet la requête du navigateur vers le nœud qui gère cette session. Une fois qu’une session est créée les requêtes issues du navigateur seront toujours envoyées vers le nœud qui a créé cette session.

Nous allons mettre en place un répartiteur de charge en utilisant Apache comme serveur HTTP frontal, et le module mod_jk comme répartiteur.

1. Installation du module mod_jk

a. Pré­requis : serveur HTTP Apache

Il vous faut d’abord installer le serveur Apache. Ce serveur est, en général, installé par défaut sur les distributions Linux. Consultez la documentation de votre distribution pour retrouver les fichiers de configuration du serveur Apache. Sous Ubuntu, vous trouverez les fichiers de configuration dans le répertoire /etc/apache2.

Sous Windows, vous pouvez télécharger le serveur Apache à l’URL www.apache.org, puis en suivant le lien HTTP Server, installer le serveur Apache2. Une fois l’installation terminée, vous trouverez les fichiers de configuration dans le répertoire C:\Program Files\Apache Software Foundation\Apache2.2\conf.

b. Téléchargement du module

Le téléchargement du module se fait à l’URL tomcat.apache.org.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzhUxF06WRzIgLAA==-enidentnumber

Cliquez sur le lien Tomcat Connectors.

Cliquez sur le lien Binary Releases.

Cliquez sur le lien qui correspond à votre système d’exploitation. Selon le système d’exploitation choisi, divers

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzhUxF06WRzIgLAA==-enidentnumber

liens peuvent être présents. Suivez les liens de la version la plus récente pour arriver à la page de téléchargement du module. Le module est sous la forme d’un fichier SO pour LINUX comme pour Windows. Le nom du fichier est du type : mod_jk­1.2.26­httpd­2.2.4.so. Renommez ce fichier en mod_jk.so.

Attention : en fonction de la version d’Apache que vous employez, le fichier à télécharger est différent. En bas de la page de téléchargement, vous trouverez une explication décrivant les compatibilités de version.

Copiez le fichier renommé dans le répertoire modules du répertoire d’installation d’Apache.

c. Configuration du module mod_jk

Créez un fichier que nous appellerons mod­jk.conf, qui contient les lignes suivantes :

# Load mod_jk module # Specify the filename of the mod_jk lib LoadModule jk_module modules/mod_jk.so # Where to find workers.properties JkWorkersFile conf/workers.properties # Where to put jk logs JkLogFile logs/mod_jk.log # Set the jk log level [debug/error/info] JkLogLevel info # Select the log format JkLogStampFormat "[%a %b %d %H:%M:%S %Y]" # JkOptions indicates to send SSK KEY SIZE JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories # JkRequestLogFormat JkRequestLogFormat "%w %V %T" # Mount your applications JkMount /jmx-console/* loadbalancer JkMount /mon_appli_web/* loadbalancer # Add shared memory # This directive is present with 1.2.10 and # later versions of mod_jk, and is needed for # for load balancing to work properly JkShmFile logs/jk.shm # Add jkstatus for managing runtime data <Location /jkstatus/> JkMount status Order deny,allow Deny from all Allow from 127.0.0.1 </Location>

Modifiez ce fichier pour l’adapter au nom de votre application Web. Dans ce fichier exemple, les lignes :

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzhUxF06WRzIgLAA==-enidentnumber

JkMount /jmx-console/* loadbalancer JkMount /mon_appli_web/* loadbalancer

indiquent au serveur Apache que les requêtes sur les URLs /jmx­console et /mon_appli_web seront transférées au répartiteur appelé iciloadbalancer.

Si vous travaillez sous LINUX et qu’il existe un répertoire du type /etc/apache2/mods­enabled, vous pouvez directement copier le fichiermod­jk.conf dans ce répertoire.

Sinon, pour les autres distributions LINUX et pour Windows, ajoutez à la fin du fichier de configuration httpd.conf du serveur Apache la ligne suivante :

Include conf/mod-jk.conf

Le fichier httpd.conf se trouve dans le répertoire conf du serveur Apache. Sous LINUX, reportez­vous à la documentation de votre distribution.

d. Configuration des nœuds dans mod_jk

La configuration du module mod_jk lui­même est effectuée via un fichier nommé workers.properties qui doit être placé dans le répertoire conf du serveur Apache.

Vous pouvez changer ce répertoire par l’intermédiaire de la directiveJkWorkersFile dans le fichier mod­jk.conf.

Dans ce fichier, nous allons déclarer les différents conteneurs de Servlet/JSP, et définir comment le suivi de session HTTP est effectué.

Le cluster est ici sur une même machine, ce qui explique les numéros de port, et les adresses IP utilisées.

# Define list of workers that will be used # for mapping requests worker.list=loadbalancer,status # Define Node1 # modify the host as your host IP or DNS name. worker.noeud1.port=8109 worker.noeud1.host=127.0.0.1 worker.noeud1.type=ajp13 worker.noeud1.lbfactor=1 # Define Node2 # modify the host as your host IP or DNS name. worker.noeud2.port=8209 worker.noeud2.host= 127.0.0.1 worker.noeud2.type=ajp13 worker.noeud2.lbfactor=1 # Load-balancing behaviour worker.loadbalancer.type=lb worker.loadbalancer.balance_workers=noeud1,noeud2 worker.loadbalancer.sticky_session=1 #worker.list=loadbalancer # Status worker for managing

Les éléments, qui traitent les requêtes, sont appelésworker. Une instance de Tomcat est un worker. Le répartiteur de charge est un autre type de worker.

Nous avons donc ici quatre workers :

noeud1 qui correspond à une instance de Tomcat ;

noeud2 qui correspond à une autre instance de Tomcat ;

loadbalancer qui correspond au répartiteur de charge ;

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzhUxF06WRzIgLAA==-enidentnumber

status qui est un worker virtuel. Il ne traite pas les requêtes HTTP, il permet de récupérer l’état et les informations de mod_jk.

Chaque worker est configuré par une suite de directives constituées de la manière suivante :

worker.<nom_du_worker>.<directive>=<valeur>

worker.list : liste les workers qui seront instanciés par mod_jk, donc les workers correspondant aux instances de Tomcat ne doivent pas apparaître ici ;

type : protocole utilisé, ici ajp13 pour les instances de Tomcat, et lb pour le répartiteur ;

host : IP ou nom de domaine de l’instance de Tomcat;

port : port d’écoute pour le protocole ;

lbfactor : pondération du répartiteur de charge, plus ce nombre est élevé, plus le conteneur de servlet recevra de requêtes;

sticky-session : permet de faire suivre la session au nœud qui l’a créée ;

balance_workers : liste des instances de Tomcat utilisées par le répartiteur de charge.

e. Configuration de JBoss

Pour chaque instance de JBoss utilisée dans le cluster, nous devons maintenant configurer le fichier server.xml du service Web. Ce fichier se trouve dans le répertoire deploy/jboss­web.deployer de votre configuration serveur.

Dans l’élément <Engine>, configurez le suivi des requêtes en précisant le nom du worker (un worker par nœud du cluster). La valeur de jvmRoute doit correspondre aux workers déclarés dans le fichier workers.properties.

<Engine name="jboss.web" defaultHost="localhost" jvmRoute="noeud1">

Et dans le fichier deploy/jboss­web.deployer/META­INF/jboss­service.xml, positionnez l’attribut UseJK.

<attribute name="UseJK">true</attribute>

f. Test final

Démarrez les différents nœuds de votre cluster. Lorsque tous les nœuds sont démarrés, démarrez le serveur Apache.

Vous pouvez maintenant atteindre votre site web par l’URL http://localhost/mon_appli_web/

Notez que vous passez maintenant par le port 80, celui sur lequel écoute Apache, et non plus par un port d’écoute de JBoss, 8180 ou 8280 dans notre exemple.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzhUxF06WRzIgLAA==-enidentnumber

JBoss RichFaces

Le projet RichFaces est une librairie de composants permettant l’intégration de comportements AJAX dans les vues, et ce, de manière très simple. Ce framework s’appuie sur les composants JSF (Java Server Face). Le projet Ajax4Jsf a été intégré au projet RichFaces. RichFaces permet aussi de définir des thèmes pour personnaliser simplement le rendu des pages.

Le comportement AJAX est très simple à mettre en place. Il faut définir :

un événement qui invoquera une requête AJAX ;

la zone de la page qui sera synchronisée après le traitement de la requête AJAX par le serveur.

Nous allons présenter ici dans un exemple très simple, la mise en place du framework.

Avant de commencer, nous devons télécharger l’ensemble des bibliothèques nécessaires :

la librairie RichFaces 3.2.x à l’URL http://www.jboss.org/jbossrichfaces/downloads/ où vous choisirez une archive avec les binaires : richfaces­ui­3.2.2.GA­bin.zip par exemple.

les librairies "commons" du site Apache que vous pouvez télécharger à http://commons.apache.org/.

Décompressez les archives récupérées dans un répertoire spécifique. Pour mieux organiser son espace de travail l’auteur regroupe ainsi toutes les librairies nécessaires aux différents projets dans un répertoire librairies_java.

L’ensemble des librairies nécessaires au projet sont les suivantes :

librairies JSF 1.2 (incluses dans la distribution JBoss)

jsf­api.jar

jsf­impl.jar

librairies RichFaces

richfaces­api­3.2.2.GA.jar

richfaces­impl­3.2.2.GA.jar

richfaces­ui­3.2.2.GA.jar

librairies Apache

commons­beanutils.jar

commons­collections.jar (incluse dans la distribution JBoss)

commons­digester.jar

commons­logging.jar (incluse dans la distribution JBoss)

Le projet présenté est dans l’archive des exemples, sous le nom "Chap 10 ­ RichFaces". Vous retrouverez dans le répertoire WEB­INF/lib, les librairies nécessaires, qui ne sont pas incluses dans la distribution JBoss.

Le fichier web.xml de l’application doit prendre en compte les librairies JSF et RichFaces.

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

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzO8H24qWRzIgLAA==-enidentnumber

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>Chap10 - RichFaces</display-name> <context-param> <param-name>org.richfaces.SKIN</param-name> <param-value>blueSky</param-value> </context-param> <filter> <display-name>RichFaces Filter</display-name> <filter-name>richfaces</filter-name> <filter-class> org.ajax4jsf.Filter </filter-class> </filter> <filter-mapping> <filter-name>richfaces</filter-name> <servlet-name>Faces Servlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> <!-- Faces Servlet --> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class> javax.faces.webapp.FacesServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- Faces Servlet Mapping --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <login-config> <auth-method>BASIC</auth-method> </login-config> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>

Les pages JSP doivent au moins comprendre les librairies de balises personnalisées suivantes :

<%@taglib uri="http://richfaces.org/a4j" prefix="a4j" %> <%@taglib uri="http://richfaces.org/rich" prefix="rich" %> <%@taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

Notre page index.jsp met en place une zone de saisie. À chaque appui sur une touche dans cette zone, une autre zone sera remplie au fur et à mesure, par des appels AJAX.

<%@ page contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@taglib uri="http://richfaces.org/a4j" prefix="a4j" %> <%@taglib uri="http://richfaces.org/rich" prefix="rich" %> <%@taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <!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>Chapitre 10 - RichFaces</title>

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzO8H24qWRzIgLAA==-enidentnumber

</head> <body> <f:view> <h:form> <rich:panel header="Exemple Echo">

<h:inputText value="#bean.text"> <a4j:support event="onkeyup" reRender="rep" />

</h:inputText> <h:outputText value="#bean.text" id="rep"/> </rich:panel> </h:form> </f:view> </body> </html>

La zone de saisie est de type inputText, et sa valeur est reliée à un Bean, que nous allons définir juste après.

La zone remplie est de type outputText, sa valeur est reliée au même Bean que la zone de saisie. Cette zone possède un attribut id, donc unique dans la page, qui vaut rep.

L’élément <rich:panel ...> permet de mettre en place une apparence de base pour notre page.

L’élément ajoute le comportement AJAX en définissant quel événement JavaScript est utilisé, et quel champ doit être mis à jour par l’attribut reRender qui contient la liste des id des champs.

Notre Bean est extrêmement simple :

public class BeanTexte private String text; public BeanTexte() public String getText() return text; public void setText(String text) this.text = text;

La prise en charge de ce Bean est effectuée par le fichier faces­config.xml qui se trouve dans le répertoire WEB­INF.

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config> <managed-bean> <managed-bean-name>bean</managed-bean-name> <managed-bean-class> fr.editions.eni.chap10.rf.BeanTexte </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>text</property-name> <value /> </managed-property> </managed-bean> </faces-config>

La propriété text du Bean est maintenant accessible par #bean.text dans la page JSP.

Une fois l’application déployée sous JBoss, elle est accessible par l’URL : http://localhost:8080/richf/index.jsf

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzO8H24qWRzIgLAA==-enidentnumber

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzO8H24qWRzIgLAA==-enidentnumber

Paramétrage et utilisation d’Eclipse

Ce chapitre ne présente qu’une prise en main rapide d’Eclipse, vous trouverez sur le site d’Eclipse la documentation complète et de nombreux tutoriaux.

La version d’Eclipse utilisée est celle dite "Europa", la nouvelle version "Ganymede" est également compatible, mais les copies d’écran présentées sont celles de la version "Europa". Eclipse peut être téléchargé à l’adresse suivante : http://www.eclipse.org

Prenez la version prévue pour le développement JEE.

Vous aurez aussi besoin de télécharger la bibliothèque XDoclet, pour les EJB2, disponible à l’adresse : http://xdoclet.sourceforge.net/xdoclet/install.html

Une fois Eclipse installé, et XDoclet décompressé, vous pouvez paramétrer Eclipse pour utiliser JBoss et XDoclet.

La première fois que vous exécuterez Eclipse, vous devrez indiquer votre répertoire de travail. C’est dans ce répertoire que tous vous projets seront sauvegardés, et c’est aussi là que vous pourrez importer les projets de l’archive contenant les exemples de ce livre.

1. Paramétrage d’Eclipse

Nous allons d’abord voir comment paramétrer Eclipse pour qu’il puisse contrôler JBoss.

Une fois Eclipse démarré, cliquez sur le menu Window ­ Preferences. Une boîte de dialogue s’affiche.

Cliquez sur l’item Server...Installed Runtime, puis sur le bouton Add.

- 1 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Recherchez le dossier contenant les serveurs JBoss et sélectionnez la version 4.2, puis cliquez sur Next.

- 2 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

À l’aide du bouton Browse, recherchez le répertoire d’installation de JBoss, vérifiez que votre JRE est bien de version 1.5 au minimum en cliquant sur le lien Installed JRE preferences. Cliquez sur Finish. Vous revenez sur la boîte de dialogue de paramétrages.

- 3 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Choisissez l’Item XDoclet, et à l’aide du bouton Browse, recherchez le répertoire où vous avez décompressé l’archive XDoclet.

Vérifiez que la version téléchargée de XDoclet correspond bien à celle utilisée.

- 4 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Cliquez maintenant sur le sous­menu ejbdocletde l’item XDoclet et sélectionnez JBoss comme tâche XDoclet supplémentaire.

Cliquez sur le bouton Edit.

- 5 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Sélectionnez la version 4.0 de JBoss, puis cliquez sur Finish, puis sur OK pour fermer la boîte de dialogue des préférences.

2. Déploiement d’un projet à partir d’Eclipse

Avant de déployer un projet, veuillez vous assurer que le serveur est bien paramétré. Il faut aussi qu’il apparaisse dans l’onglet Servers de votre espace de travail.

Pour ajouter JBoss à votre configuration de travail, effectuez un clic droit sur le plan de travail de l’onglet Servers, puis choisissez New...Server. L’assistant suivant s’affiche.

- 6 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Choisissez le serveur JBoss que nous avons installé précédemment. Cliquez sur Next, l’écran suivant nous montre les paramètres par défaut.

- 7 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Nous gardons ces paramètres. Cliquez sur le bouton Finish. Le serveur JBoss apparaît maintenant dans l’onglet Servers.

Vous pouvez le contrôler par les icônes :

- 8 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

la flèche dans le rond vert permet de démarrer le serveur ;

l’image d’insecte permet de le démarrer en mode débogage, et donc de faire du pas à pas sur des points d’arrêt.

Durant la phase de démarrage, la console affiche les messages du serveur :

Vous pouvez ajouter votre projet au serveur. Revenez sur l’onglet Servers et effectuez un clic droit sur le serveur JBoss. Puis sélectionnez dans le menu contextuel Add and remove projects... un assistant vous permet de gérer les projets à déployer.

- 9 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

La partie droite présente les projets déployés, la partie gauche liste les projets disponibles et non déployés.

Lorsque le projet est déployé, les modifications sur ce projet déclencheront automatiquement un redéploiement.

Les projets déployés apparaissent maintenant dans l’onglet Servers, sous le serveur dans lesquels ils sont déployés, JBoss dans notre cas.

3. Import de projet existant dans votre espace de travail

Sélectionnez dans le menu l’item File...Import, une boîte de dialogue s’ouvre.

- 10 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Sélectionnez l’item Existing Projects into Workspace dans l’arborescence General.

Cliquez sur le bouton Next. Une autre boîte de dialogue s’ouvre :

- 11 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Avec le bouton Browse, recherchez le répertoire où vous avez décompressé l’archive des exemples téléchargés sur le site de ENI. Sélectionnez les projets que vous souhaitez importer, ils sont tous sélectionnés par défaut.

Cochez le sélecteur Copy projects into workspace pour que les projets soient réellement copiés dans votre espace de travail.

Cliquez sur le bouton Finish. Les projets devraient maintenant apparaître dans l’explorateur de projets de votre espace de travail.

4. Créer un nouveau projet

a. Projet EJB 2

Cliquez sur le menu File ­ New ­ Project. Un assistant de création de projet s’ouvre.

- 12 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Ouvrez le dossier EJB, sélectionnez EJB Project, puis cliquez sur Next.

- 13 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Donnez un nom à votre projet, sélectionnez le serveur JBoss.

Comme configuration, sélectionnez EJB Project with XDoclet.

- 14 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Cochez les cases EJB Module, EJBDoclet (XDoclet) et Java.

Vérifiez les versions utilisées.

Cliquez sur Finish, ou sur Next puis Finish. Le dernier écran de l’assistant présente le dossier où seront mis les sources du module EJB.

Votre projet est créé et apparaît dans l’explorateur de projets. Il est en erreur car les fichiers XML ne sont pas complets, ce qui est normal car vous n’avez pas encore créé d’EJB.

Sélectionnez votre projet, et par un clic droit sur le projet sélectionné, choisissez dans le menu contextuel l’item : New...XDoclet Enterprise JavaBean.

Un assistant s’ouvre.

Création d’un EJB 2 avec XDoclet

- 15 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Sélectionnez Session Bean pour cet exemple. Puis cliquez sur le bouton Next. La fenêtre suivante s’ouvre.

Si vous n’avez pas créé de package pour votre projet, vous pouvez le saisir sur cette fenêtre. Donnez un nom à votre EJB, ce nom doit finir par Bean, dans notre exemple notre classe se nommera CalculetteBean. Cliquez sur le bouton Next.

- 16 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

La fenêtre suivante de l’assistant propose les paramètres par défaut, que nous garderons ici. Ces paramètres peuvent être changés ensuite dans la classe CalculetteBean, au niveau des commentaires XDoclet. Cliquez sur Finish, ou Next puis Finish dans l’écran suivant.

Dans la console, vous devez voir apparaître les sorties du script Ant qui générera les classes via XDoclet.

Dans votre projet, l’ensemble des classes et des fichiers XML doit maintenant être créé.

Par défaut, une méthode métier existe dans la classe CalculetteBean, la méthode public String foo(String param).

/** * * <!-- begin-xdoclet-definition --> * @ejb.interface-method view-type="remote"

* <!-- end-xdoclet-definition --> * @generated

* * //TODO: Must provide implementation for bean method stub

*/ public String foo(String param)

return null;

Pour ajouter votre propre méthode, vous reprendrez l’ensemble des commentaires XDoclet et du code pour les adapter à votre besoin.

Ajout d’une méthode métier

- 17 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

/** * * <!-- begin-xdoclet-definition --> * @ejb.interface-method view-type="remote"

* <!-- end-xdoclet-definition --> * @generated

* * //TODO: Must provide implementation for bean method stub

*/ public int aditionner(int a, int b)

return a+b;

Lors de la sauvegarde de votre classe, la génération XDoclet va démarrer et mettre à jour l’ensemble des fichiers java et xml.

La visibilité de votre méthode est par défaut, "remote", si vous souhaitez la changer, vous devez mettre à jour la valeur de "view-type" :

remote : la méthode accessible à distance uniquement (via RMI) ;

local : la méthode est accessible localement uniquement (dans la même machine virtuelle) ;

both : la méthode est accessible à distance et localement.

* <!-- begin-xdoclet-definition --> * @ejb.interface-method view-type="both"

* <!-- end-xdoclet-definition -->

b. Projet EJB 3

Cliquez sur le menu File ­ New ­ Project. L’assistant de création de projet s’ouvre. Sélectionnez le projet EJB comme précédemment, puis cliquez sur Next.

- 18 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Donnez un nom à votre projet, vérifiez que la configuration du projet est bien Default Configuration for JBoss v4.2, puis cliquez sur le bouton Next.

- 19 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Pour les projets en EJB 3, vous devez sélectionner une JDK 5 ou 6. Ici, nous n’utilisons pas XDoclet. Cliquez sur Finish, votre projet est maintenant créé.

c. Projet Web

Cliquez sur le menu File ­ New ­ Project. L’assistant de création de projet s’ouvre.

- 20 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Sélectionnez le projet Dynamic Web Project dans le répertoire Web, puis cliquez sur Next.

- 21 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Donnez un nom à votre projet. Laissez les configurations proposées et cliquez sur le bouton Next.

- 22 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Vérifiez les versions, puis cliquez sur Next.

- 23 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Donnez un nom d’application Web dans la zone de saisie Context Root. C’est ce nom qui sera utilisé dans l’URL. Pour l’exemple ci­dessus, l’URL serait : http://localhost:8080/test. Vérifiez que la case à cocher Generate Deployment Descriptor est activée. Cliquez sur le bouton Next. Votre projet est maintenant créé.

d. Projet d’entreprise

Nous allons maintenant réunir un projet EJB et un projet Web dans une même application d’entreprise.

Cliquez sur le menu File ­ New ­ Project. L’assistant de création de projet s’ouvre.

- 24 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Sélectionnez Enterprise Application Project dans l’arborescence J2EE, puis cliquez sur le bouton Next.

- 25 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Donnez un nom à votre projet, laissez les configurations proposées, puis cliquez sur le bouton Next.

- 26 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Nous laisserons la version 5 comme niveau de spécification. Cliquez sur le bouton Next.

- 27 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Les projets ouverts sont affichés, nous pouvons choisir ceux qui feront partie de l’application d’entreprise en les sélectionnant. Cochez la case Generate deployment Descriptor, puis cliquez sur le bouton Finish. Votre projet est créé. Le projet EAR ne fait que regrouper d’autres projets. Il ne comporte pas de fichier java.

e. Création d’une archive

Eclipse peut aussi exporter un projet sous forme d’un fichier archive adéquat : jar, war ou ear en fonction du projet.

Le projet à exporter doit être ouvert. Il suffit alors de le sélectionner, d’effectuer un clic droit sur la sélection et de choisir l’item Export du menu contextuel.

5. Résoudre quelques problèmes courants

a. Erreurs suite à un import

Si des erreurs se produisent lors de l’import des projets, cela est souvent le fait de mauvais classpath, ou d’emplacements de librairies qui sont différents d’un projet à l’autre. Cela se traduit par l’apparition d’une croix rouge au niveau du projet.

Si l’erreur est une erreur de syntaxe, des croix rouges marquent les classes qui sont en erreur.

- 28 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Vous pouvez aussi voir l’ensemble des erreurs dans l’onglet Problems de votre espace de travail.

Pour résoudre les erreurs liées à des problèmes de classpath, vous devez sélectionner votre projet dans l’explorateur de projets. Puis par un clic droit sur le projet sélectionné, venez chercher le dernier item Properties, une boîte de dialogue s’ouvre.

Sélectionnez le menu Java Build Path et cliquez sur l’onglet Libraries, vérifiez que toutes les librairies sont bien référencées. Si ce n’est pas le cas, elles sont marquées en erreur. Dans l’exemple ci­dessus, la librairie "JRE System" est marquée en erreur.

Sélectionnez la librairie en erreur et supprimez­la en cliquant sur le bouton Remove.

Ensuite, cliquez sur le bouton Add Library, une nouvelle boîte de dialogue s’ouvre.

- 29 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Dans notre cas, nous sélectionnons la librairie JRE System Library, puisque c’est celle­ci qui est en erreur. Ensuite, cliquez sur le bouton Next. Une boîte de dialogue de choix de la JRE s’ouvre.

Sélectionnez votre librairie et cliquez sur Finish.

b. Certaines vues ne sont pas disponibles

Eclipse vous permet de voir votre projet au travers de vues très différentes : explorateur de projets, explorateur de fichiers, éditeur de code, éditeur de fichier XML...

Eclipse comporte plusieurs perspectives qui correspondent à des vues et éditeurs courants pour un type de projet et un type de travail sur ce projet : perspectives Debug, Java, JEE, etc. Le nombre de perspectives peut varier en fonction des plug­ins que vous utilisez.

Une perspective contient des vues et des éditeurs. Les vues nous permettent de naviguer et sélectionner certaines parties de notre projet. Les éditeurs permettent de modifier et visualiser des éléments de notre projet.

Si une de vos vues a été fermée par inadvertance, vous pouvez la retrouver par le menu : Window ­ Show View. Une liste des vues les plus utilisées est directement accessible. Si la vue désirée ne se trouve pas dans la liste, cliquez sur Other. Une boîte de dialogue des vues s’ouvre alors.

- 30 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

Les vues sont organisées par thème, ouvrez un thème pour trouver la vue appropriée, sélectionnez­la et cliquez sur OK.

Le nombre de vues étant important, vous pouvez aussi faire une recherche par filtrage des vues. Il faut indiquer le type de vue dans la zone de saisie, en saisissant un mot­clé en anglais. Par exemple si vous cherchez les vues de type "navigateur", tapez "Navigator".

De manière équivalente, vous pouvez ouvrir une nouvelle perspective, soit par le menu Window ­ Open Perspective, qui vous permet de choisir une perspective parmi celles qui sont les plus utilisées, soit par l’item Other pour avoir l’ensemble des perspectives présentes.

Vous pouvez aussi utiliser les raccourcis situés au­dessous des boutons systèmes de la fenêtre.

- 31 -© ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber

- 32 - © ENI Editions - All rigths reserved - Kaiss Tag

enidentnumber-AAEAAAD/////AQAAAAAAAAAMAgAAAE1FTkkuRWRpdGlvbnMuTUVESUFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VOSS5FZGl0aW9ucy5NRURJQXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAA5MzczMzc5IC0gS2Fpc3MgVGFnIC0gZWNhNTA5YTUtMDdkMC00NDU1LTgzYjItZGFmOTViNjc0ZWMzKfsQBqaRzIgLAA==-enidentnumber