JBoss Enterprise Application Platform 5 Guide d`utilisateur de JBoss
Transcription
JBoss Enterprise Application Platform 5 Guide d`utilisateur de JBoss
JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer à utiliser dans JBoss Enterprise Application Platform 5 Édition 5.1.0 Mark Newton Aleš Justin JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer à utiliser dans JBoss Enterprise Application Platform 5 Édition 5.1.0 Mark Newto n Red Hat mark.newto n@jbo ss.o rg Aleš Justin Red Hat [email protected] m Publié par Misty Stanley-Jo nes Red Hat [email protected] m Note légale Copyright © 2011 Red Hat, Inc. T his document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed. Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law. Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the Infinity Logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries. Linux ® is the registered trademark of Linus T orvalds in the United States and other countries. Java ® is a registered trademark of Oracle and/or its affiliates. XFS ® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries. MySQL ® is a registered trademark of MySQL AB in the United States, the European Union and other countries. Node.js ® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project. T he OpenStack ® Word Mark and OpenStack Logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community. All other trademarks are the property of their respective owners. Résumé Ce guide est destiné aux développeurs Java qui souhaitent utiliser Jboss Microcontainer pour déployer des environnements Java modulaires et personnalisés pour leurs applications Table des matières Table des matières .Préface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . 1. Conventions d'écriture 4 1.1. Conventions typographiques 4 1.2. Conventions pour citations mises en avant 5 1.3. Notes et avertissements 6 2. Obtenir de l'aide et faire des commentaires 7 2.1. Avez-vous besoin d'aide ? 7 2.2. Vos commentaires sont importants ! 7 . . . . . . . I.. .Introduction Partie . . . . . . . . . . . . . au . . . Microcontainer . . . . . . . . . . . . . . . . -. .T. utoriel . . . . . . . dirigé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9. . . . . . . . . . .Chapitre . . . . . . . . .1. . . Pré-requis . . . . . . . . . . . .avant . . . . . . d'utiliser . . . . . . . . . .ce . . .Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 ............ 1.1. Installer Maven 10 1.2. Configuration Maven spéciale pour les exemples de Microcontainer 13 1.3. Décharger les exemples 14 .Chapitre . . . . . . . . .2. . . Introduction . . . . . . . . . . . . . .sur . . . la . . .Microcontainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 ............ 2.1. Caractéristiques 16 2.2. Définitions 16 2.3. Installation 17 .Chapitre . . . . . . . . .3. . . Services . . . . . . . . . .de . . .construction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 ............ 3.1. Introduction à l'exemple Ressources Humaines 18 3.2. Compiler l'exemple de projet HRManager 19 3.3. Créer des POJO 19 3.3.1. Descripteurs de déploiement XML 19 3.4. Connecter les POJO ensemble. 19 3.4.1. Considérations spéciales 20 3.5. T ravailler avec des services 20 3.5.1. Configuration d'un service 21 3.5.2. T ester un service 21 3.5.3. Packager un service 23 .Chapitre . . . . . . . . .4. . .Utiliser . . . . . . . les . . . .services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 ............ 4.1. Amorçage de Microcontainer 28 4.2. Déployer le service 29 4.3. Accès direct 30 4.4. Accès indirect 32 4.5. Le chargement de classes dynamique 33 4.5.1. Problems With Classloaders Created with Deployment Descriptors 38 .Chapitre . . . . . . . . .5. . . Ajout . . . . . . de . . . .comportement . . . . . . . . . . . . . . .AOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .39 ........... 5.1. Créer un aspect 39 5.2. Configurer le Microcontainer pour AOP 41 5.3. Appliquer un Aspect 43 5.4. Lifecycle Callbacks 45 5.5. Ajout de Recherche de service (Look-ups) par JNDI 47 . . . . . . . II. Partie . . .Concepts . . . . . . . . . .avancés . . . . . . . . . de . . . Microcontainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. .9. . . . . . . . . . .Chapitre . . . . . . . . .6. . . Modèles . . . . . . . . . .de . . .composants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 ............ 6.1. Interactions permises avec les modèles de composants 50 6.2. Un bean sans dépendances 50 6.3. Utilisation de Microcontainer avec Spring 50 1 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer 6.3. Utilisation de Microcontainer avec Spring 6.4. Utilisation de Guice avec le Microcontainer 6.5. Mbeans hérités, et comment mixer différents modèles de composants 6.6. Exposer les POJO en tant que MBeans 50 51 53 54 .Chapitre . . . . . . . . .7. . . Injection . . . . . . . . . .de . . .dépendances . . . . . . . . . . . . . . avancées . . . . . . . . . . .et . . .IoC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 ............ 7.1. Usine de valeurs 57 7.2. Callbacks 59 7.3. Mode d'accès de bean 61 7.4. Bean Alias 62 7.5. Support d'annotations XML (ou Métadonnées) 62 7.6. Autowire 65 7.7. Usine de bean 65 7.8. Constructeur de métadonnées de beans 68 7.9. Personnalisation du chargeur de classes. 69 7.10. Mode Controller 70 7.11. Cycle 71 7.12. Offre et demande 72 7.13. Installs 72 7.14. Lazy Mock 73 7.15. Cycle de vie 74 .Chapitre . . . . . . . . .8. ..T . .he . . .Virtual . . . . . . . File . . . . .System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 ............ 8.1. VFS Public API 78 8.2. VFS Architecture 87 8.3. Existing Implementations 87 8.4. Extension Hooks 88 8.5. Features 88 .Chapitre . . . . . . . . .9. . . La . . . .couche . . . . . . . .de . . .chargement . . . . . . . . . . . . .de . . .classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 ............ 9.1. Chargeur de classes 90 9.2. Chargement de classes 97 9.3. ClassLoading VFS 102 .Chapitre . . . . . . . . .10. . . . .T. he . . . Virtual . . . . . . . .Deployment . . . . . . . . . . . . Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104 ............. 10.1. Agnostic Handling of Deployment T ypes 104 10.2. Separation of Structure Recognition From Deployment lifecycle logic 104 10.3. Natural Flow Control in the form of Attachments 107 10.4. Client, User, and Server Usage and Implementation Details 108 10.5. Single State Machine 108 10.6. Scanning Classes for Annotations 109 . . . . . . . . . . . .de Historique . . .révision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 ............. 2 Table des matières 3 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Préface 1. Conventions d'écriture Ce manuel utilise plusieurs conventions pour souligner l'importance de certains mots ou expressions, mais aussi en vue d'attirer l'attention sur certains passages d'informations précis. Pour les éditions sur support papier et numérique (PDF), ce manuel utilise des caractères issus de Liberation Fonts. La police de caractères Liberation Fonts est également utilisée pour les éditions HT ML si elle est installée sur votre système. Sinon, des polices de caractères alternatives équivalentes sont utilisées. Notez que Red Hat Enterprise Linux 5 et versions supérieures contiennent la police Liberation Fonts par défaut. 1.1. Conventions typographiques Quatre conventions typographiques sont utilisées pour attirer l'attention sur certains mots et expressions. Ces conventions et les circonstances auxquelles elles s'appliquent sont les suivantes. Caractères gras à espacem ent fixe Utilisé pour surligner certaines entrées du système, y compris les commandes shell, les noms de fichiers et les chemins d'accès. Également utilisé pour surligner les touches et les combinaisons de touches. Par exemple : Pour consulter le contenu du fichier m on_nouvel_ouvrage_littéraire qui se situe dans votre dossier courant, saisissez la commande cat m on_nouvel_ouvrage_littéraire à la demande du terminal et appuyez sur Entrée pour exécuter la commande. L'exemple ci-dessus contient un nom de fichier, une commande shell et une touche, tous présentés sous forme de caractères gras à espacement fixe et tous bien distincts grâce au contexte. Les combinaisons de touches se distinguent des touches individuelles par le signe « plus », qui connecte les différentes parties de la combinaison. Par exemple : Appuyez sur Entrée pour exécuter la commande. Appuyez sur Ctrl+Alt+F2 pour basculer sur un terminal virtuel. Le premier exemple présente une touche particulière sur laquelle appuyer. Le second exemple affiche une combinaison de touches : un ensemble de trois touches sur lesquelles il faut appuyer simultanément. Si le code source est mentionné, les noms de classes, les méthodes, les fonctions, les noms de variables et les valeurs de retour citées dans un paragraphe seront présentées comme ci-dessus, en caractères gras à espacem ent fixe. Par exemple : Les classes de fichiers comprennent le nom de classe filesystem pour les noms de fichier, file pour les fichiers et dir pour les dossiers. Chaque classe correspond à un ensemble de permissions associées. Caractères gras proportionnels Cette convention marque le surlignage des mots ou phrases que l'on rencontre sur un système, comprenant des noms d'application, des boîtes de dialogue textuelles, des boutons étiquettés, des 4 Préface cases à cocher et des boutons d'options mais aussi des intitulés de menus et de sous-menus. Par exemple : Sélectionnez Système → Préférences → Souris à partir de la barre du menu principal pour lancer les Préférences de la souris. À partir de l'onglet Boutons, cliquez sur la case à cocher Pour gaucher puis cliquez sur Ferm er pour faire passer le bouton principal de la souris de la gauche vers la droite (ce qui permet l'utilisation de la souris par la main gauche). Pour insérer un caractère spécial dans un fichier gedit, choisissez Applications → Accessoires → T able des caractères depuis la barre du menu principal. Ensuite, choisissez Recherche → T rouver… depuis la barre du menu T able des caractères, saisissez le nom du caractère dans le champ Recherche puis cliquez sur Suivant. Le caractère recherché sera surligné dans la T able des caractères. Double-cliquez sur le caractère surligné pour le placer dans le champ T exte à copier, puis cliquez sur le bouton Copier. Vous pouvez désormais revenir à votre document et choisir Modifier → Coller depuis la barre du menu gedit. Le texte ci-dessus contient des noms d'applications, des noms de menus et d'autres éléments s'appliquant à l'ensemble du système, des boutons et textes que l'on trouve dans une interface graphique. Ils sont tous présentés sous la forme gras proportionnel et identifiables en fonction du contexte. Italique gras à espacement fixe ou Italique gras proportionnel Qu'ils soient en caractères gras à espacement fixe ou à caractères gras proportionnels, l'ajout de l'italique indique la présence de texte remplaçable ou variable. Les caractères en italique indiquent la présence de texte que vous ne saisissez pas littéralement ou de texte affiché qui change en fonction des circonstances. Par exemple : Pour se connecter à une machine distante en utilisant ssh, saisissez ssh nom d'utilisateur@ domain.name (nom.domaine) après l'invite de commande de la console. Si la machine distante est exem ple.com et que votre nom d'utilisateur pour cette machine est john, saisissez ssh john@ exam ple.com . La commande m ount -o rem ount système de fichiers monte le système de fichiers nommé. Ainsi, pour monter /hom e dans le système de fichiers, la commande est m ount -o rem ount /hom e. Pour connaître la version d'un paquet actuellement installé, utilisez la commande rpm -q paquet. Elle vous permettra de retourner le résultat suivant : version-de-paquet. Remarquez que les mots en gras italique ci-dessus — username (nom d'utilisateur), domain.name (nom.domaine), file-system (système de fichiers), package (paquetage), version et release (sortie commerciale). Chaque mot est un espace réservé au texte, soit pour le texte que vous entrez lors de la saisie d'une commande, soit pour le texte affiché par le système. Mis à part l'utilisation habituelle de présentation du titre d'un ouvrage, les caractères italiques indiquent l'utilisation initiale d'un terme nouveau et important. Ainsi : Publican est un système de publication DocBook. 1.2. Conventions pour citations mises en avant Les sorties de terminaux et les citations de code source sont mis en avant par rapport au texte avoisinant. 5 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer avoisinant. Les sorties envoyées vers un terminal sont en caractères Rom ains à espacem ent fixe et présentées ainsi : books books_tests Desktop Desktop1 documentation downloads drafts images mss notes photos scripts stuff svgs svn Les citations de code source sont également présentées en rom ains à espacem ent fixe mais sont présentés et surlignés comme suit : package org.jboss.book.jca.ex1; import javax.naming.InitialContext; public class ExClient { public static void main(String args[]) throws Exception { InitialContext iniCtx = new InitialContext(); Object ref = iniCtx.lookup("EchoBean"); EchoHome home = (EchoHome) ref; Echo echo = home.create(); System.out.println("Created Echo"); System.out.println("Echo.echo('Hello') = " + echo.echo("Hello")); } } 1.3. Notes et avertissements Enfin, nous utilisons trois styles visuels pour attirer l'attention sur des informations qui auraient pu être normalement négligées : Note Une remarque est une forme de conseil, un raccourci ou une approche alternative par rapport à une tâche à entreprendre. L'ignorer ne devrait pas provoquer de conséquences négatives, mais vous pourriez passer à côté d'une astuce qui vous aurait simplifiée la vie. Important Les blocs d'informations importantes détaillent des éléments qui pourraient être facilement négligés : des modifications de configurations qui s'appliquent uniquement à la session actuelle ou des services qui ont besoin d'être redémarrés avant toute mise à jour. Si vous ignorez une case étiquetée « Important », vous ne perdrez aucunes données mais cela pourrait être source de frustration et d'irritation. 6 Préface Avertissement Un avertissement ne devrait pas être ignoré. Ignorer des avertissements risque fortement d'entrainer des pertes de données. 2. Obtenir de l'aide et faire des commentaires 2.1. Avez-vous besoin d'aide ? Si vous rencontrez des difficultés avec l'une des procédures décrites dans cette documentation, veuillez visiter le Portail Client Red Hat sur http://access.redhat.com. Sur le portail client, vous pourrez : effectuer des recherches ou naviguer sur la base de connaissances d'articles de support techniques concernant les produits Red Hat. soumettre une requête de support au service Red Hat Global Support Services (GSS). accéder aux documents des autres produits de Red Hat. Red Hat est l'hôte de nombreuses listes de diffusion traitant de ses logiciels et technologies. Vous trouverez un ensemble des listes de diffusion disponibles au public sur https://www.redhat.com/mailman/listinfo. Cliquez sur le nom d'une liste pour vous inscrire à celle-ci ou pour accéder à ses archives. 2.2. Vos commentaires sont importants ! Si vous repérez une erreur de typographie dans ce guide, ou si vous pensez à un moyen de parfaire ce guide, faîtes-nous en part ! Soumettez-nous un rapport dans Bugzilla sous le produit JBoss Enterprise Application Platform 5 et sous le composant docJBoss_Microcontainer_User_Guide. Le lien suivant vous conduira vers un format de rapport tout préparé pour le produit dont il s'agit : http://bugzilla.redhat.com/. Remplissez le formulaire suivant dans Bugzilla sous le champ Description. Veillez à être aussi précis que possible quand vous décrivez le problème; cela nous permettra de régler le problème plus rapidement. URL du Document : Numéro et Nom de Section : Description du problème : Suggestions pour améliorer : Informations supplémentaires : N'oubliez pas de mentionner votre nom de façon à ce que nous puissions vous donner tout le crédit que vous méritez à soulever le problème en question. 7 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer 8 Partie I. Introduction au Microcontainer - Tutoriel dirigé Partie I. Introduction au Microcontainer - Tutoriel dirigé 9 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Chapitre 1. Pré-requis avant d'utiliser ce Guide Pour utiliser les exemples de ce guide, vous aurez besoin d'installer et de configurer des logiciels de support, et décharger le code pour les exemples. 1.1. Installer Maven Les exemples utilisés dans ce projet nécessitent Maven v2.2.0 ou version supérieure. Décharger Maven directement de la page d'accueil d'Apache Maven, puis installer et configurer votre système comme expliqué dansProcédure 1.1, « Installer Maven ». Procédure 1.1. Installer Maven 1. Vérifiez que Java Developer Kit 1.6 ou version supérieure est installé. Il s'agit également d'un pré-requis pour Enterprise Platform. Vérifiez que Java est installé dans votre système et que vous avez installé la variable d'environnement JAVA_HOME dans votre ~/.bash_profile pour Linux, ou dans Propriété Système de Windows. Pour obtenir davantage d'informations sur les variables d'environnement, voir l'étape Étape 4 de cette procédure. 2. Décharger Maven Note Cette étape et les étapes suivantes assument que vous avez sauvegardé Maven dans la location recommandée pour votre système d'exploitation. Maven, à la manière de toute autre application Java, peut être installé dans n'importe quelle location raisonnable de votre système. Visit http://maven.apache.org/download.html. Cliquer sur le lien d'archives compressées et compilées apache-m aven-2.2.1-bin.zip Sélectionner un miroir de déchargement à partir de la liste. Pour les utilisateurs Linux Sauvegarder l'archive compressée dans votre répertoire hom e. Pour les utilisateurs Windows Sauvegarder l'archive compressée dans votre répertoire C:\Docum ents and Settings\user_name. 3. Installer Maven Pour les utilisateurs Linux Extraire le fichier compressé dans votre répertoire hom e. Si vous sélectionnez l'archive compressée à l'étape 2, et que vous ne renommez pas le répertoire, le répertoire extrait est nommé apache-m aven-version. Pour les utilisateurs Windows Extraire l'archive compressée de C:\Program Files\Apache Software Foundation. Si vous avez sélectionné l'archive compressée à l'étape 2, et que vous ne renommez pas le répertoire, le 10 Chapitre 1. Pré-requis avant d'utiliser ce Guide répertoire extrait sera nommé apache-m aven-version. 4. Configurer les variables d'environnement Pour les utilisateurs Linux Ajouter les lignes suivantes à votre profil ~/.bash_profile. Changer le nom d'utilisateur [username] pour qu'il corresponde à votre nom d'utilisateur, et pour que le répertoire Maven corresponde bien au nom du répertoire. Le numéro de version peut être différent du numéro listé ci-dessous. export M2_HOME=/home/[username]/apache-maven-2.2.1 export M2=$M2_HOME/bin export PATH=$M2:$PATH En insérant M2 au début de votre chemin d'accès, la version Maven que vous venez d'installer correspondra à la version à utiliser par défaut. Vous voudrez sans doute également définir le chemin d'accès de votre variable d'environnement JAVA_HOME dans la location du JDK de votre système. Pour les utilisateurs Windows Ajouter les variables d'environnement M2_HOME, M2, et JAVA_HOME. a. Appuyer sur Start+Pause|Break. La boîte de dialogue des Propriétés Système est affichée. b. Cliquer sur l'onglet Advanced, puis sur le bouton Environm ent Variables. c. Sous System Variables, sélectionner Path. d. Cliquer sur Edit, et ajouter à la fin les deux chemins d'accès Maven un point virgule pour séparer chaque entrée. Les point d'interrogation ne sont pas requis autour des chemins. Ajouter la variable M2_HOME et définir le chemin à C:\Program Files\Apache Software Foundation\apache-m aven-2.2.1. Ajouter la variable M2 et définir la valeur à %M2_HOME%\bin. e. Dans la même boîte de dialogue, créer la variable d'environnement JAVA_HOME : Ajouter la variable %JAVA_HOME% et définir la valeur de la location pour votre JDK. Par exemple, C:\Program Files\Java\jdk1.6.0_02. f. Dans le même dialogue, mettez à jour ou créez la variable d'environnement de chemin d'accès : Ajouter la variable %M2% pour permettre à Maven d'être exécuté à partir de la ligne de commande. Ajouter la varaible %JAVA_HOME%\bin pour définir le chemin d'accès vers l'installation Java qui convient. g. Cliquer sur OK jusqu'à ce que la boîte de dialogues System Properties se ferme. 5. Modifier .bash_profile Pour les utilisateurs Linux uniquement Pour mettre à jour les changements apportés à .bash_profile dans la session courante, sourcer votre .bash_profile. [localhost]$ source ~/.bash_profile 11 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer 6. Mettre à jour votre profil de terminal gnome Pour les utilisateurs Linux uniquement Mettez à jour le profil de terminal pour veiller à ce que les itérations suivantes du terminal gnome (ou terminal Konsole) puisse lire les mêmes variables. a. Cliquer sur Edit → Profiles b. Sélectionner Default, et cliquer sur le bouton Edit. c. Dans la boîte de dialogue Editing Profile, cliquer sur l'onglet T itle and Com m and. d. Sélectionner la case Run com m and as login shell. e. Fermer toutes les boîtes de dialogue ouvertes du T erminal. 7. Vérifier les changements de variables d'environnement et l'installation Maven Pour les utilisateurs Linux Pour s'assurer que les changements ont été implémentés correctement, ouvrir un terminal et exécuter les commandes suivantes : Exécuter echo $M2_HOME, qui devrait retourner le résultat suivant. [localhost]$ echo $M2_HOME /home/username/apache-maven-2.2.1 Exécuter echo $M2, qui devrait retourner le résultat suivant. [localhost]$ echo $M2 /home/username/apache-maven-2.2.1/bin Exécuter echo $PAT H, et vérifier que le répertoire Maven /bin soit inclus. [localhost]$ echo $PATH /home/username/apache-maven-2.2.1/bin Exécutez which m vn, qui doit afficher le chemin d'accès à l'exécutable au fichierMaven executable. [localhost]$ which mvn ~/apache-maven-2.2.1/bin/mvn Exécuter m vn -version, qui doit afficher la version Maven, la version Java associée, et les informations de système d'exploitation. [localhost]$ $ mvn -version Apache Maven 2.2.1 (r801777; 2009-08-07 05:16:01+1000) Java version: 1.6.0_0 Java home: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre Default locale: en_US, platform encoding: UTF-8 OS name: "Linux" version: "2.6.30.9-96.fc11.i586" arch: "i386" Family: "unix" Pour les utilisateurs Windows Pour vérifier que les changements ont été implémentés correctement, ouvrir un terminal et exécuter la commande suivante : Dans une invite de commande, exécuter m vn -version 12 Chapitre 1. Pré-requis avant d'utiliser ce Guide C:\> mvn -version Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700) Java version: 1.6.0_17 Java home: C:\Sun\SDK\jdk\jre Default locale: en_US, platform encoding: Cp1252 OS name: "windows xp" version: "5.1" arch: "x86" Family: "windows" Vous avez maintenant configuré Maven avec succès pour utilisation avec les exemples de ce guide. 1.2. Configuration Maven spéciale pour les exemples de Microcontainer Maven est un système de génération modulaire qui extrait les dépendances selon les besoins. Les exemples de ce guide assument que vous avez inclus le bloc d'XML dans Exemple 1.1, « Exemple de fichier settings.xm l » dans votre ~/.m 2/settings.xm l (Linux) ou C:\Docum ents and Settings\username\.m 2\settings.xm l (Windows). Si le fichier n'existe pas déjà, vous pouvez commencer par le créer. 13 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 1.1. Exemple de fichier settings.xm l <settings> <profiles> <profile> <id>jboss.repository</id> <activation> <property> <name>!jboss.repository.off</name> </property> </activation> <repositories> <repository> <id>snapshots.jboss.org</id> <url>http://snapshots.jboss.org/maven2</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>repository.jboss.org</id> <url>http://repository.jboss.org/maven2</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>repository.jboss.org</id> <url>http://repository.jboss.org/maven2</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>snapshots.jboss.org</id> <url>http://snapshots.jboss.org/maven2</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> </settings> 1.3. Décharger les exemples Les exemples de ce guide vous montrent comment créer un projet Maven qui dépend de JBoss Microcontainer, en utilisant Maven. Vous pouvez les décharger à partir de images/examples.zip . Cette location peut changer, mais est incluse pour l'occasion. Une fois que vous aurez déchargé le fichier Z IP qui contient les exemples, l'extraire dans une location pratique et regardez les exemples pour vous familiariser avec leur structure. 14 Chapitre 1. Pré-requis avant d'utiliser ce Guide 15 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Chapitre 2. Introduction sur la Microcontainer Le Microcontainer de JBoss est une refactorisation du micronoyau JMX JBoss pour supporter le déploiement POJO direct et autonome en dehors du serveur d'applications de JBoss. \t\nLe Microcontainer est conçu pour répondre aux besoins spécifiques des développeurs Java qui souhaitent utiliser des techniques de programmation orientées objet pour déployer rapidement des logiciels. En outre, il permet aux logiciels d'être déployés sur une vaste gamme de matériel, de platesformes informatiques mobiles, aux environnements de calcul en réseau à grande échelle et tout le reste. 2.1. Caractéristiques T outes les caractéristiques du micronoyau JMX Déploiement POJO direct (nul besoin de Standard/XMBean ou MBeanProxy) Injection de dépendance de style IOC directe Amélioration de la gestion du cycle de vie Contrôle supplémentaire des dépendances Intégration AOP transparente Système de fichiers virtuel Framework de déploiement virtuel Chargement de classes OSGi 2.2. Définitions Ce guide utilise certains termes qui ne sont sans doute pas familiers. Certains sont définis dans Liste de définitions du Microcontainer. Liste de définitions du Microcontainer Micronoyau JMX Le micronoyau JMX JBoss est un environnement modulaire de Java. La différence avec un environnement régulier comme J2EE, c'est que le développeur est en mesure de choisir exactement quels composants doivent faire partie de son environnement et quels composants abandonner. POJO Un Plain Old Java Object (POJO) est un objet modulaire, réutilisable Java. Le nom est utilisé pour souligner qu'un objet donné est un Objet Java ordinaire, et non pas un objet spécial, et plus particulièrement, pas un JavaBean Enterprise. Le terme fut inventé par Martin Fowler, Rebecca Parsons et Josh MacKenzie en Septembre 2000 à l'occasion d'une discussion au cours de laquelle ils soulignèrent les avantages d'encoder la logique commerciale dans des objets java réguliers plutôt due dans des beans d'entité. Java Bean Un Java Bean est un composant de logiciel réutilisable qui peut être manipulé visuellement par un outil de constructeur. Un Java Bean est un morceau de code indépendant. Il ne doit pas hériter d'une interface ou classe de base particulière. Malgré que les Java Beans sont principalement créés en IDE graphiques, ils peuvent également être développés dans de simples éditeurs de texte. 16 Chapitre 2. Introduction sur la Microcontainer AOP Aspect-Oriented Programming (AOP) est un paradigme de programmation, dans lequel des fonctions secondaires ou de support sont isolées de la logique commerciale du programme principal. Il s'agit d'un sous-ensemble de la programmation orientée-objet. 2.3. Installation Le Microcontainer fait partie intégrale de la plateforme Enterprise. Vous trouverez davantage d'informations sur la façon d'installer et de configurer Enterprise Platform dans le Guide d'administration et de configuration. 17 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Chapitre 3. Services de construction Les services sont des morceaux de code qui réalisent des tâches utiles à plusieurs clients. Dans le but qui nous intéresse, nous allons ajouter plusieurs contraintes à la définition d'un service. Les services doivent posséder des noms uniques qui pourront être référencés ou appelés par les clients. Les aspects internes d'un service doivent être invisibles et ne doivent pas être d'aucune importance au client. Il s'agit du concept de la "black box" OOP (Object-oriented programming). Dans OOP, chaque objet est indépendant, et aucun autre objet n'a besoin de savoir comment il effectue son travail. Dans le contexte précis d'un Microcontainer, les services sont construits à partir des POJO. Un POJO est pratiquement un service en lui-même, mais on ne peut y accéder par un nom unique, et il doit être créé par le client qui en a besoin. Malgré qu'un POJO doit être créé en cours d'exécution par un client, il n'a pas besoin d'être implémenté par une classe séparée pour pouvoir fournir une interface bien définie. Dans la mesure ou les champs et les méthodes ne sont pas retirées, et que l'accès n'est pas limité, vous n'aurez pas besoin de recompiler les clients pour qu'ils utilisent une nouveau POJO. Note L'implémentation d'une interface est uniquement nécessaire pour permettre au client de choisir entre des implémentations différentes. Si le client est compilé avec une interface, on peut fournir plusieurs implémentations de l'interface sans avoir à recompiler le client. L'interface veille à ce que les signatures de méthodes ne changent pas. Le reste de ce guide consiste à créer un service de Ressources Humaines, avec le Microcontainer pour capturer et modulariser la logique commerciale de l'application. Une fois que le Microcontainer est installé, l'exemple de code se situe dans le fichier suivant exam ples/User_Guide/gettingStarted/hum anResourcesService. 3.1. Introduction à l'exemple Ressources Humaines Au fur et à mesure que vous vous familiarisez avec la structure de répertoire de fichiers de l'exemple, notez qu'il utiliser le Maven Standard Directory Layout. Les fichiers source de Java se situent dans les packages qui se toruvent dans le répertoire exam ples/User_Guide/gettingStarted/hum anResourcesService/src/m ain/java/org/j boss/exam ple/service, après que vous ayez extrait le fichier Z IP. Chacune de ces classes représente un simple POJO qui n'implémente aucune interface spéciale. La classe la plus importante est HRManager, qui représente le point d'entrée du service qui fournit toutes les méthodes publiques que les clients vont appeler. Méthodes fournies par la classe HRManager addEm ployee(Employee employee) rem oveEm ployee(Employee employee) getEm ployee(String firstName, String lastName) getEm ployees() getSalary(Employee employee) setSalary(Employee employee, Integer newSalary) isHiringFreeze() 18 Chapitre 3. Services de construction setHiringFreeze(boolean hiringFreeze) getSalaryStrategy() setSalaryStrategy(SalaryStrategy strategy) \t\nLe Service des ressources humaines est composé d'une poignée de classes qui maintiennent une liste des employés et leurs coordonnées (adresses et salaires, dans ce cas). Par l'interface SalaryStrategy , il est possible de configurer le HRManager de sorte que des implémentations de stratégie de salaire différents soient disponibles pour placer des limites minimales et maximales sur les salaires pour les rôles différents d'employés. 3.2. Compiler l'exemple de projet HRManager Afin de compiler le code source, lancez m vn com pile à partir du répertoire hum anResourcesService/. Cela va créer un nouveau répertoire intitulé target/classesqui contient les classes compilées. Pour nettoyer le projet et retirer le répertoire cible, lancez la commande m vn clean. 3.3. Créer des POJO Avant qu'un POJO puisse être utilisé, vous devrez le créer. Vous aurez besoin d'un mécanisme de nommage qui vous permette d'enregistrer une référence à l'instance du POJO par un nom. Les clients ont besoin d'utiliser le POJO. Le Microcontainer fournit un tel mécanisme : un Controller. Le contrôleur vous permet de déployer vos services basés-POJO dans un environnement en cours d'exécution. 3.3.1. Descripteurs de déploiement XML Après la compilation des classes, utilisez un descripteur de déploiement de XML pour en créer des instances. Le descripteur contient une liste de beans représentant des instances individuelles. Chaque bean a un nom unique, afin qu'il puisse être appelée par le client au moment de l'exécution. Le descripteur suivant déploie une instance du HRManager : <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"/> </deployment> Cet XML crée une instance de la classe HRManager et l'enregistre avec le nom HRService. Ce fichier est alors passé à un déployeur XML associé à un Microcontainer en cours d'exécution, qui procède au même déploiement, et instancie les beans. 3.4. Connecter les POJO ensemble. Les instances POJO individuelles ne fournissent que de relativement simples comportements. La puissance réelle des POJO provient du fait qu'on peut les connecter ensemble pour entreprendre des taches complexes. Comment connecter des POJO ensemble pour choisir des implémentations de stratégie de salaires différentes ? C'est ce que le descripteur de déploiement XML fait : 19 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> </bean> <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"/> </deployment> Cet XML crée une instance de l'application de la stratégie choisie salaire en incluant un élément <bean> supplémentaire. Cette fois, AgeBasedSalaryStrategy est choisi. Ensuite le code injecte une référence à ce bean dans l'instance de HRManager créé à l'aide du bean HRService. L'injection est possible parce que la classe HRManager contient une méthode setSalaryStrategy(SalaryStrategy strategy). Dans les coulisses, JBoss Microcontainer appelle cette méthode sur l'instance de HRManager nouvellement créée et passe une référence à l'instance AgeBasedSalaryStrategy. Le descripteur de déploiement XML entraîne la même séquence d'événements comme si vous aviez écrit le code suivant : HRManager hrService = new HRManager(); AgeBasedSalaryStrategy ageBasedSalary = new AgeBasedSalaryStrategy(); hrService.setSalaryStrategy(ageBasedSalary); En plus de faire des injections par les méthodes de setter de propriété, JBoss Microcontainer peut également faire des injections par des paramètres de constructeur si nécessaire. Pour plus de détails sur la question, consulter le chapitre 'Injection' dans la partie II 'Développement de POJO'. 3.4.1. Considérations spéciales Malgré qu'il soit possible de créer des instances de classes par l'élément <bean> du descripteur de déploiement, ce n'est pas toujours le meilleur moyen. Ainsi, créer des instances des classes Em ployee et Address n'est pas nécessaire, parce que le client les crée en réponse à une saisie de la part de l'utilisateur. Ils font partie du service mais ne sont pas répertoriés dans le descripteur de déploiement. Commenter votre code. Vous pouvez définir des beans multiples au sein d'un descripteur de déploiement tant qu'ils ont chacun a un nom unique, qui est utilisé pour effectuer l'injection comme indiqué ci-dessus. Cependant tous les beans ne représentent pas nécessairement tous des services. Alors qu'un service peut être implémenté à l'aide d'un seul bean, les beans sont généralement utilisés en combinaison. Un bean représente typiquement le point d'entrée du service et contient les méthodes publiques appelées par les clients. Dans cet exemple, le point d'entrée est le bean HRService. Le descripteur de déploiement XML n'indique pas si un bean représente un service ou s'il est le point d'entrée de service. C'est une bonne idée d'utiliser des commentaires et un schéma de nommage évident pour délimiter les beans de service des beans de non-service. 3.5. Travailler avec des services Après avoir créé des POJO et les avoir connecté ensemble pour former des services, vous aurez besoin de configurer les services, de les tester et de les mettre en packages. 20 Chapitre 3. Services de construction 3.5.1. Configuration d'un service Les services peuvent être configurés de deux façon différentes au moins : Injecter des références entre les instances de POJO Injecter des valeurs dans les propriétés de POJO Dans cet exemple, la seconde méthode est utilisée. Le descripteur de déploiement suivant configure l'instance du HRManager des façons suivantes : Un gel d'embauche est implémenté. AgeBasedSalaryStrategy implémente les nouvelles valeurs de salaire minimum et maximum. L'injection des références entre les instances de POJO est une façon de configurer un service. Cependant, nous pouvons également injecter des valeurs dans les propriétés POJO. Le descripteur de déploiement suivant nous montre comment configurer l'instance HRManager pour avoir un gel d'embauche et pour que le AgeBasedSalaryStrategy ait des valeurs de salaire minimum et maximum : <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"> <property name="hiringFreeze">false</property> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> </bean> <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"> <property name="minSalary">1000</property> <property name="maxSalary">80000</property> </bean> </deployment> Les classes doivent avoir des méthodes de setter publiques pour les propriétés qui conviennent, de façon à pouvoir injecter les valeurs. Par exemple, la classe HRManager possède une méthode setHiringFreeze(boolean hiringFreeze) et la classe AgeBasedSalaryStrategy possède les méthodes setMinSalary(int m inSalary) et setMaxSalary(int m axSalary). Les valeurs du descripteur de déploiement sont converties à partir de strings dans les types qui conviennent (booléen, int etc...) par les PropertyEditors JavaBeans. De nombreux PropertyEditors sont fournis par défaut pour les types standards, mais vous pouvez créer le vôtre si nécessaire. Voir le chapitre Propriétés dans la Partie II 'Développement de POJO' pour plus de détails. 3.5.2. Tester un service Une fois que vous avez créé vos POJO et que vous les avez connecté pour former vos services, vous aurez besoin de les tester. JBoss Microcontainer permet aux unités de tester les POJO individuels, ainsi que les services, par l'utilisation d'une classe MicrocontainerT est. \t\nLa classe org.jboss.test.kernel.junit.MicrocontainerT esthérite de junit.fram ework.T estCase, configurant chaque test par un amorçage de JBoss Microcontainer et en ajoutant un BasicXMLDeployer. Elle recherche alors le chemin de classe pour un descripteur de déploiement de XML avec le même nom que la classe de test, se terminant par .xm l et résidant dans 21 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer une structure de répertoires qui représente le nom du package de la classe. Les beans qui se trouvent dans ce fichier sont déployés et peuvent ensuite être consultés en utilisant une méthode pratique appelée getBean(String nam e). Vous pourrez trouver des descripteurs de déploiement dans Exemple 3.1, « Listing du répertoire src/test/resources ». Exemple 3.1. Listing du répertoire src/test/resources ├── log4j.properties └── org └── jboss └── example └── service ├── HRManagerAgeBasedTestCase.xml ├── HRManagerLocationBasedTestCase.xml ├── HRManagerTestCase.xml └── util ├── AgeBasedSalaryTestCase.xml └── LocationBasedSalaryTestCase.xml Le code de test se situe dans le répertoire src/test/java : Exemple 3.2. Listing du répertoire src/test/java └── org └── jboss └── example └── service ├── HRManagerAgeBasedTestCase.java ├── HRManagerLocationBasedTestCase.java ├── HRManagerTestCase.java ├── HRManagerTest.java ├── HRManagerTestSuite.java └── util ├── AgeBasedSalaryTestCase.java ├── LocationBasedSalaryTestCase.java └── SalaryStrategyTestSuite.java \t\nLa classe HRManagerT est étend MicrocontainerT est afin d'installer un certain nombre d'employés à utiliser comme base pour les tests, des cas de tests individuels, puis sous-classe HRManagerT est, pour effectuer un travail réel. Quelques classes de T estSuite sont également utilisées pour grouper des cas individuels de test pour l'aspect pratique. Pour exécuter les tests, saisir m vn test à partir du répertoire hum anResourcesService/. Vous devriez voir des sorties de log DEBUG qui montre que JBoss Microcontainerqui démarre et qui déploie les beans du fichier XML qui convient, avant d'exécuter chaque test. En fin de tests, les beans ne sont plus déployés et le Microcontainer est désactivé. 22 Chapitre 3. Services de construction Note Certains tests comme HRManagerT estCase, AgeBasedSalaryT estCase, et LocationBasedSalaryT estCase, font des tests unitaires de POJO individuels. D'autres tests comme HRManagerAgeBasedT estCase et HRManagerLocationBasedT estCase font des tests unitaires de services entiers. Dans tous les cas, les tests sont exécutés de la même manière. Utiliser la classe MicrocontainerT est facilite l'installation et la réalisation de tests en profondeur de toute partie de votre code. Les classes Address et Em ployee ne sont pas testées ici. C'est à vous d'écrire des tests pour eux. 3.5.3. Packager un service Après avoir testé votre service, il est temps de le mettre en paquet, afin que d'autres personnes puissent l'utiliser. La façon la plus simple de le faire est de créer un JAR contenant toutes les classes. Vous pouvez choisir d'inclure le descripteur de déploiement, s'il y a une bonne façon de configurer le service par défaut, mais c'est optionnel. Procédure 3.1. Mettre un service en package 1. Mettez le descripteur de déploiement dans le répertoire MET A-INF (en option). Si vous choisissez d'inclure le descripteur de déploiement, par convention, il doit être nommé jboss-beans.xm l et devra se trouver dans un répertoire MET A-INF. Il s'agit de la structure par défaut pour Enterprise Platform, pour que le déployeur de JAR reconnaisse cette structure et puisse effectuer le déploiement automatiquement. Le descripteur de déploiement n'est pas inclus dans l'exemple de Ressources Humaines, parce que le service est configuré en modifiant le descripteur directement, sous forme d'un fichier séparé. 2. Générer le JAR Pour générer un JAR qui contient toutes les classes compilées, saisir m vn package dans le répertoire hum anResourcesService/. 3. Rendez le JAR disponible aux autres projets Maven. Pour rendre le JAR disponible auprès des autres projets Maven, saisir m vn install pour le copier dans votre référentiel Maven local. La structure finale du JAR se trouve dans Exemple 3.3, « Listing of the org/jboss/exam ple/service and MET A-INFDirectories ». 23 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 3.3. Listing of the org/jboss/exam ple/service and MET A-INFDirectories `-- org `-- jboss `-- example `-- service |-- Address.java |-- Employee.java |-- HRManager.java `-- util |-- AgeBasedSalaryStrategy.java |-- LocationBasedSalaryStrategy.java `-- SalaryStrategy.java `--META-INF `-- MANIFEST.MF `-- maven `-- org.jboss.micrcontainer.examples `-- humanResourceService Note Le répertoire MET A-INF/m aven est créé automatiquement par Maven, et ne sera pas présent si vous utilisez un autre système de construction. 24 Chapitre 4. Utiliser les services Chapitre 4. Utiliser les services Le chapitre précédent vous a guidé vers les création, configuration, test et mise en package d'un service. La prochaine étape consiste à créer un client qui va en fait produire un travail en utilisant un service. Le client, dans cet exemple, utilise une interface T UI Text User Interface (TUI) pour accepter l'entrée de l'utilisateur et les résultats de sortie. Cela réduit la taille et la complexité de l'exemple de code. T ous les fichiers nécessaires sont situés dans le répertoire exam ples/User_Guide/gettingstarted/com m andLineClient, qui suit le Maven Standard Directory Layout, comme on le voit dans Exemple 4.1, « Listing pour le répertoire exam ples/User_Guide/gettingstarted/com m andLineClient ». Exemple 4 .1. Listing pour le répertoire exam ples/User_Guide/gettingstarted/com m andLineClient ├── ├── │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └── pom.xml src ├── main │ ├── assembly │ │ ├── aop.xml │ │ ├── classloader.xml │ │ ├── common.xml │ │ └── pojo.xml │ ├── config │ │ ├── aop-beans.xml │ │ ├── classloader-beans.xml │ │ ├── pojo-beans.xml │ │ └── run.sh │ ├── java │ │ └── org │ │ └── jboss │ │ └── example │ │ └── client │ │ ├── Client.java │ │ ├── ConsoleInput.java │ │ ├── EmbeddedBootstrap.java │ │ └── UserInterface.java │ └── resources │ └── log4j.properties └── test ├── java │ └── org │ └── jboss │ └── example │ └── client │ ├── ClientTestCase.java │ ├── ClientTestSuite.java │ └── MockUserInterface.java └── resources └── jboss-beans.xml target └── classes └── log4j.properties Le client consiste en trois classes et une interface, qui se situent dans le répertoire 25 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer org/jboss/exam ple/client.\n UserInterface décrit les méthodes appelées par le client en cours d'exécution pour demander des actions de la part de l'utilisateur.ConsoleInput est une implémentation de UserInterface qui crée un T UI utilisé par l'utilisateur pour communiquer avec le client. L'avantage de ce concept est que vous pouvez facilement créer une implémentation Swing de UserInterface par la suite, et remplacer le T UI par un GUI. Vous pouvez également simuler le processus de saisie des données par un script. Vous pourrez alors vérifier le comportement des clients automatiquement par des cas de test JUnit conventionnels que vous pourrez trouver dans Exemple 3.2, « Listing du répertoire src/test/java ». Pour une génération réussie, vous devrez tout d'abord construire, et installer auditAspect.jar à partir du répertoire exam ples/User_Guide/gettingStarted/auditAspect par le biais de la commande m vn install com m and. Plusieurs distributions client seront créées, y compris une basée AOP qui compte sur la présence de auditAspect.jar dans le répertoire Maven. Si vous avez déjà saisi m vn install du répertoire exam ples/User_Guide/gettingStarted alors hum anResourcesService.jar et auditAspect.jar auront déjà été générés et mis en package, avec le client, donc cette étape ne sera pas nécessaire. Pour compiler le code source, toutes les étapes de Procédure 4.1, « Compiler le Code Source » sont franchies quand vous lancez la commande m vn package du répertoire com m andLineClient. Procédure 4 .1. Compiler le Code Source 1. Exécuter les tests d'unités. 2. Générer un JAR client. 3. Assembler une distribution qui contient tous les fichiers utiles. Après avoir compilé et empaqueté le client, la structure du répertoire dans le répertoire com m andLineClient/target inclut les sous-répertoires décrits dans Exemple 4.2, « Sousrépertoires du répertoire com m andLineClient/target. ». Exemple 4 .2. Sous-répertoires du répertoire com m andLineClient/target. client-pojo utilisé pour appeler le service sans AOP. client-cl utilisé pour démontrer les fonctionnalités de chargement de classes. client-aop Ajouter le support AOP. Voir Chapitre 5, Ajout de comportement AOP pour obtenir des détails supplémentaires. Chaque sous-répertoire représente une distribution différente, qui contient tous les scripts shell, les JAR, et les descripteurs de déploiement XML utiles pour exécuter le client dans les configuraitons variées. Le reste de ce chapitre est basé sur la distribution client-pojo que l'on toruve dans le sous-répertoire client-pojo, listé dans Exemple 4.3, « Listing du répertoire client-pojo ». 26 Chapitre 4. Utiliser les services Exemple 4 .3. Listing du répertoire client-pojo |-|-|-| | | | | | | | | | | | `-- client-1.0.0.jar jboss-beans.xml lib |-- concurrent-1.3.4.jar |-- humanResourcesService-1.0.0.jar |-- jboss-common-core-2.0.4.GA.jar |-- jboss-common-core-2.2.1.GA.jar |-- jboss-common-logging-log4j-2.0.4.GA.jar |-- jboss-common-logging-spi-2.0.4.GA.jar |-- jboss-container-2.0.0.Beta6.jar |-- jboss-dependency-2.0.0.Beta6.jar |-- jboss-kernel-2.0.0.Beta6.jar |-- jbossxb-2.0.0.CR4.jar |-- log4j-1.2.14.jar `-- xercesImpl-2.7.1.jar run.sh Pour exécuter le client, déplacez-vous vers le répertoire client-pojo et saisissez ./run.sh. Exemple 4.4, « Écran Menu HRManager » apparaîtra. Exemple 4 .4 . Écran Menu HRManager Menu: d) Deploy Human Resources service u) Undeploy Human Resources service a) l) r) g) s) t) Add employee List employees Remove employee Get a salary Set a salary Toggle hiring freeze m) Display menu p) Print service status q) Quit > Pour sélectionner une option, saisir la lettre qui se situe sur la gauche et appuyer sur RET URN. Par exemple, pour afficher les options du menu, saisir m , suivi par RET URN. Saisir plus d'une lettre ou saisir une option qui n'est pas valide résultera par un message erreur. 27 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Important Le script run.sh définit l'environnement d'exécution en ajoutant tous les JAR qui se trouvent dans le répertoire lib/ dans le chemin de classes par la propriété java.ext.dirs. Il ajoute également le répertoire courant et le client-1.0.0.jar par l'intermédiaire de l'indicateur -cp de façon à ce que le descripteur de déploiement jboss-beans.xm l puisse être localisé en cours d'exécution, ainsi que la classe org.jboss.exam ple.client.Client qui est appelée pour démarrer l'application. 4.1. Amorçage de Microcontainer Avant d'utiliser le client pour déployer et appeler votre service, regardez ce qui se passe quand il est généré. public Client(final boolean useBus) throws Exception { this.useBus = useBus; ClassLoader cl = Thread.currentThread().getContextClassLoader(); url = cl.getResource("jboss-beans.xml"); // Start JBoss Microcontainer bootstrap = new EmbeddedBootstrap(); bootstrap.run(); kernel = bootstrap.getKernel(); controller = kernel.getController(); bus = kernel.getBus(); } T out d'abord un URL représentant le descripteur de déploiement jboss-beans.xm l est créé. Plus tard, il sera nécessaire que le déployeur XML puisse déployer ou supprimer le déploiement des beans déclarés dans le fichier. La méthode getResource() du chargeur de classes d'application est utilisée parce que le fichier jboss-beans.xm l est inclus sur le chemin de classe. Cela est optionnel. Le nom et la location du descripteur de déploiement importent peu tant que l'URL est valide et accessible. Ensuite, une instance de JBoss Microcontanier est créée, avec un déployeur XML. Ce processus s'appelle bootstrapping et une classe de convénience BasicBootstrap est fournie dans le Microcontainer pour permettre la configuration programmatique. Pour ajouter un déployeur XML, étendre BasicBootstrap pour créer une classe Em beddedBootstrap et remplacer la méthode bootstrap() protégée comme suit : 28 Chapitre 4. Utiliser les services public class EmbeddedBootstrap extends BasicBootstrap { protected BasicXMLDeployer deployer; public EmbeddedBootstrap() throws Exception { super(); } public void bootstrap() throws Throwable { super.bootstrap(); deployer = new BasicXMLDeployer(getKernel()); Runtime.getRuntime().addShutdownHook(new Shutdown()); } public void deploy(URL url) { ... deployer.deploy(url); ... } public void undeploy(URL url) { ... deployer.undeploy(url); ... } protected class Shutdown extends Thread { public void run() { log.info("Shutting down"); deployer.shutdown(); } } } Le crochet shutdown veille qu'à la sortie de JVM, tous les déploiements de beans soient annulés dans l'ordre qui convient. Les méthodes publiques deploy/undeploy délèguent au BasicXMLDeployer de façon à ce que les beans déclarés dans jboss-beans.xm l puissent être déployés ou que leur déploiement puisse être supprimé. Finalement, les références au bus et au contrôleur du Microconteneur seront restaurées, donc vous pourrez rechercher les références de bean par nom et y accéder directement ou indirectement si nécessaire. 4.2. Déployer le service Après avoir créé le service, vous pourrez déployer le service de Ressources Humaines. Vous pouvez procéder en saisissant l'option d de l'interface utilisateur. La sortie indique que BasicXMLDeployer a pu traiter le fichier jboss-beans.xm l par lURL, et en a instancié les beans. 29 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Note Le Microconteneur est en mesure d'instancier les beans parce que leurs classes sont disponibles dans l'extension du chemin de classe qui se trouve à l'intérieur du fichier lib/hum anResourcesService.jar. Vous pouvez également mettre ces classes dans une structure de répertoire explosée et l'ajouter au chemin de classe de l'application, mais il est plus pratique de les mettre en paquet dans un JAR. Le descripteur de déploiement est complètement séparé du fichier hum anResourcesService.jar. Cela permet d'effectuer des changements rapides pour les tests. Le fichier jboss-beans.xm l de l'exemple contient certains fragments de XML, dont les commentaires ont été supprimés, qui montrent quelques unes des configurations possibles. <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="HRService" class="org.jboss.example.service.HRManager"> <!-- <property name="hiringFreeze">true</property> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> --> </bean> <!-- <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"> <property name="minSalary">1000</property> <property name="maxSalary">80000</property> </bean> <bean name="LocationBasedSalary" class="org.jboss.example.service.util.LocationBasedSalaryStrategy"> <property name="minSalary">2000</property> <property name="maxSalary">90000</property> </bean> --> </deployment> Important Selon la façon dont vous accédez au service au moment de l'exécution, vous devrez peut-être arrêter l'application et la redémarrez à nouveau pour redéployer le service et de voir vos modifications. Cela réduit la flexibilité de l'application, mais résulte en une exécution plus rapide. Alternativement, vous pourriez simplement redéployer le service en cours d'exécution de l'application. Ceci accroît la flexibilité mais résulte en un ralentissement de la performance. Gardez ces compromis en mémoire quand vous créez vos applications. 4.3. Accès direct 30 Chapitre 4. Utiliser les services Si aucun paramètre n'est donné au script run.sh au démarrage du client, on cherche un bean HRService par l'intermédiaire du contrôleur du Microconteneur, une fois que le service est déployé. private HRManager manager; ... private final static String HRSERVICE = "HRService"; ... void deploy() { bootstrap.deploy(url); if (!useBus && manager == null) { ControllerContext context = controller.getInstalledContext(HRSERVICE); if (context != null) { manager = (HRManager) context.getTarget(); } } } Plutôt que de chercher tout de suite une référence pour l'instance de bean, l'exemple cherhce tout d'abord une référence dans un ControllerContext, puis obtient une référence à l'instance de bean à partir du contexte, en utilisant la méthode getT arget(). Be bean peut exister dans le Microcontainer sous n'importe quel état listé dans État d'un bean à l'intérieur de Microcontainer. État d'un bean à l'intérieur de Microcontainer NOT _INST ALLED DESCRIBED INST ANT IAT ED CONFIGURED INST ALLED T o keep track of which state the bean is in, wrap it in another object called a context that describes the current state. T he name of the context is the same as the bean name. Once a context reaches the INST ALLED state, the bean it represents is considered to be deployed. After creating a reference to the bean instance representing the service entry point, you can call methods on it to preform work: @SuppressWarnings("unchecked") Set<Employee> listEmployees() { if (useBus) ... else return manager.getEmployees(); } T he client is accessing the service directly since it is using a reference to the actual bean instance. Performance is good, because each method call goes directly to the bean. What happens, however, if 31 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer you want to reconfigure the service and redeploy it while the application is running? On obtient une reconfiguration en modifiant le descripteur de déploiement de XML et en enregistrant le fichier. Afin de redéployer le service, l'instance actuelle doit être annulée. Lors de l'annulation du déploiement, le contrôleur Microcontainer libère sa référence à l'instance de bean, ainsi que tous les beans dépendantes. Ces beans pourront, par la suite, être nettoyés de la mémoire, parce qu'ils ne soient plus requis par l'application. Redéployez le service crée de nouvelles instances de bean représentant la nouvelle configuration. T oute recherche qui suivra, de la part des clients, permettra de récupérer les références à ces nouvelles instances et ils seront en mesure d'accéder au service reconfiguré. Le problème, c'est que la référence à l'instance de bean représentant notre point d'entrée de service est mis en cache lorsque vous déployez le service pour la première fois. L'annulation du déploiement du service n'a aucun effet, puisque l'instance de bean peut toujours être accessible par la référence en mémoire cache et ne sera pas nettoyée avant que le client la libère. Dans la même ligne, le déploiement à nouveau du service n'entraîne pas une autre recherche parce que le client a déjà une référence en mémoire cache. Il continuera donc à utiliser l'instance de bean représentant la configuration initiale de service. Vous pouvez tester ce comportement en saisissant la lettre u suivie par RET URN pour retirer le déploiement du service en cours. Vous devez toujours pouvoir être en mesure d'accéder au service à partir du client, même s'il n'est pas déployé. Puis, faîtes quelques changements à la configuration, en utilisant le fichier jboss-beans.xm l, en l'enregistrant, et en le déployant à nouveau par l'option d. Afficher le statut du service par l'option p montre que le client est toujours en train d'accéder à l'instance initiale du service qui a été déployé. Avertissement Même si modifiez le client pour pouvoir rechercher une nouvelle référence à chaque fois que le service est déployé à nouveau, les nouveaux développeurs peuvent distribuer des copies de cette référence à d'autres objets, par erreur. Si ces références ne sont pas nettoyées pendant le redéploiement, le même problème cache peut se reproduire. Pour pouvoir redéployer le service reconfiguré de manière fiable, fermer l'application complètement par l'option 'q' et redémarrer la par le script run.sh. Pour les services Enterprise comme T ransactions, Messaging et Persistence, ce comportement est parfaitement acceptable, puisqu'ils sont généralement toutjours utilisés. Ils ne peuvent pas être redéployés en cours d'exécution et peuvent bénéficier d'une performance élevée par accès direct. Si votre service tombe dans cette catégorie, considérez l'accès direct via le contrôleur de Microcontainer. 4.4. Accès indirect Le script run.sh peut être appelé avec un paramètre en option bus, qui entraîne les appels au service des Ressources Humaines à utiliser le bus du Microcontainer. Au lieu d'utiliser une référence directe à l'instance de bean qui provient du contrôleur du Microcontainer, le nouveau comportement consiste à appeler une méthodeinvoke() sur le bus, en lui passant le nom du bean, le nom et arguments de la méthode, et les types de méthode. Le bus utilise cette information pour appeler le bean de la part du client. 32 Chapitre 4. Utiliser les services private final static String HRSERVICE = "HRService"; ... @SuppressWarnings("unchecked") Set<Employee> listEmployees() { if (useBus) return (Set<Employee>) invoke(HRSERVICE, "getEmployees", new Object[] {}, new String[] {}); else return manager.getEmployees(); } private Object invoke(String serviceName, String methodName, Object[] args, String[] types) { Object result = null; try { result = bus.invoke(serviceName, methodName, args, types); } catch (Throwable t) { t.printStackTrace(); } return result; } Le bus recherche la référence à l'instance de bean nommée et appelle la méthode choisie par réflexion. Le client ne possède jamais une référence directe à l'instance de bean, donc on dit qu'il accède à la fonction indirectement. Étant donné que le bus ne cache pas la référence, vous pouvez apporter des modifications en toute sécurité à la configuration du service, et il peut être redéployé en cours d'exécution. Les appels suivants par le client utiliseront la nouvelle référence, comme on s'y attend. Le client et le service ont été découplés. Note Ce comportement peut être testé en déployant le service et en utilisant l'option p pour imprimer le statut. Retirer le déploiement sur le service en utilisant l'option u et vous remarquerez qu'il n'est pas accessible. Puis, procédez à quelques changements au fichier jboss-beans.xm l, enregistrez les changements, et déployez-le à nouveau avec l'option d. IAfficher le statut à nouveau par l'option p. Le client aura accès à la nouvelle configuration du service. Comme le bus utilise la réflexion pour appeler des instances de bean, c'est plus lent que l'accès direct. L'avantage de l'approche, c'est que seul le bus possède des références aux instances bean. Lorsqu'un service est redéployé, toutes les références existantes peuvent être nettoyées et remplacées par de nouvelles. De cette façon, un service peut être redéployé de façon fiable en cours d'exécution. Les services qui ne sont pas très souvent utilisés ou qui sont spécifiques à certaines applications sont bons candidats à l'accès indirect par le bus du Microcontainer. Souvent, la réduction des performances est supplantée par l'avantage de la souplesse. 4.5. Le chargement de classes dynamique Jusqu'à ce point, vous avez utilisé l'extension et les chargeurs de classe de l'application pour charger toutes les classes de l'application. Le chemin de classe de l'application est défini par le script run.sh avec l'indicateur -cp pour inclure le répertoire courant dans client-1.0.0.jar, comme montré ici : 33 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1 Pour plus de commodité, les JAR du répertoire lib ont été ajoutés au chemin de classe de l'extension du chargeur de classes, à l'aide de la propriété de système java.ext.dirs, plutôt que de lister le chemin d'accès complet vers chacun des JAR après l'indicateur -cp. Comme l'extension de classloader est parente de l'application classloader, le client est est mesure de trouver toutes les classes du Microconteneur et du service des Ressources Humaines en cours d'exécution. Note Dans les versions Java 6 et versions supérieures, vous pouvez utiliser une expression de caractère générique pour inclure tous les JAR dans un répertoire par l'indicateur -cp: java -cp `pwd`/lib/* :.:client-1.0.0.jar org.jboss.exam ple.client.Client $1 Ici, toutes les classes de l'application seront ajoutées au chemin de classe du chargeur de classes d'application, et le chemin de classe de l'extension du chargeur de classes conservera sa valeur par défaut. What happens if you need to deploy an additional service at run-time? If the new service is packaged in a JAR file, it must be visible to a classloader before any of its classes can be loaded. Because you have already set up the classpath for the application classloader (and extension classloader) on start-up, it is not easy to add the URL of the JAR. T he same situation applies if the service classes are contained in a directory structure. Unless the top-level directory is located in the current directory (which is on the application classpath) then the classes will not be found by the application classloader. If you wish to redeploy an existing service, changing some of its classes, you need to work around security constraints, which forbid an existing classloader from reloading classes. T he goal is to create a new classloader that knows the location of the new service's classes, or that can load new versions of an existing service's classes, in order to deploy the service's beans. JBoss Microcontainer uses the <classloader> element in the deployment descriptor to accomplish this. T he client-cl distribution contains the file listed in the Exemple 4.5, « Listing of the com m andLineClient/target/client-cl Directory » . 34 Chapitre 4. Utiliser les services Exemple 4 .5. Listing of the com m andLineClient/target/client-cl Directory |-- client-1.0.0.jar |-- jboss-beans.xml |-- lib | |-- concurrent-1.3.4.jar | |-- jboss-common-core-2.0.4.GA.jar | |-- jboss-common-core-2.2.1.GA.jar | |-- jboss-common-logging-log4j-2.0.4.GA.jar | |-- jboss-common-logging-spi-2.0.4.GA.jar | |-- jboss-container-2.0.0.Beta6.jar | |-- jboss-dependency-2.0.0.Beta6.jar | |-- jboss-kernel-2.0.0.Beta6.jar | |-- jbossxb-2.0.0.CR4.jar | |-- log4j-1.2.14.jar | `-- xercesImpl-2.7.1.jar |-- otherLib | `-- humanResourcesService-1.0.0.jar |`-- run.sh T he hum anResourcesService.jar file has been moved to a new sub-directory called otherLib. It is no longer available to either the extension or application classloaders whose classpaths are setup in the run.sh script: java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1 T o work around this, create a new classloader during the deployment of the service, load it in the service classes, and create instances of the beans. T o see how this is done, look at the contents of the jbossbeans.xm l file: 35 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="URL" class="java.net.URL"> <constructor> <parameter>file:/Users/newtonm/jbossmc/microcontainer/trunk/docs/examples/User_Gui de/gettingStarted/commandLineClient/target/clientcl.dir/otherLib/humanResourcesService-1.0.0.jar</parameter> </constructor> </bean> <bean name="customCL" class="java.net.URLClassLoader"> <constructor> <parameter> <array> <inject bean="URL"/> </array> </parameter> </constructor> </bean> <bean name="HRService" class="org.jboss.example.service.HRManager"> <classloader><inject bean="customCL"/></classloader> <!-- <property name="hiringFreeze">true</property> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> --> </bean> <!-- <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"> <property name="minSalary">1000</property> <property name="maxSalary">80000</property> </bean> <bean name="LocationBasedSalary" class="org.jboss.example.service.util.LocationBasedSalaryStrategy"> <property name="minSalary">2000</property> <property name="maxSalary">90000</property> </bean> --> </deployment> 1. First, create an instance of java.net.URL called URL, using parameter injection in the constructor to specify the location of the hum anResourcesService.jar file on the local filesystem. 2. Next, create an instance of a URLClassLoader by injecting the URL bean into the constructor as the only element in an array. 3. Include a <classloader> element in your HRService bean definition and inject the custom CL bean. T his specifies that the HRManager class needs to be loaded by the customCL classloader. You need a way to decide which classloader to use for the other beans in the deployment. All beans in 36 Chapitre 4. Utiliser les services the deployment use the current thread's context classloader. In this case the thread that handles deployment is the main thread of the application which has its context classloader set to the application classloader on start-up. If you wish, you can specify a different classloader for the entire deployment using a <classloader> element, as shown in Exemple 4.6, « Specifying a Different Classloader ». Exemple 4 .6. Specifying a Different Classloader <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <classloader><inject bean="customCL"/></classloader> <bean name="URL" class="java.net.URL"> <constructor> <parameter>file:/Users/newtonm/jbossmc/microcontainer/trunk/docs/examples/User_G uide/gettingStarted/commandLineClient/target/clientcl.dir/otherLib/humanResourcesService-1.0.0.jar</parameter> </constructor> </bean> <bean name="customCL" class="java.net.URLClassLoader"> <constructor> <parameter> <array> <inject bean="URL"/> </array> </parameter> </constructor> </bean> ... </deployment> T his would be necessary to allow for reconfiguration of the service by uncommenting the AgeBasedSalary or LocationBasedSalary beans. Classloaders specified at the bean level override the deployment level classloader. T o override the deployment level classloader altogether, and use the default classloader for a bean, use the <null/> value as follows: <bean name="HRService" class="org.jboss.example.service.HRManager"> <classloader><null/></classloader> </bean> 37 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer 4.5.1. Problems With Classloaders Created with Deployment Descriptors If you create a new classloader for your service using the deployment descriptor, you may not be able to access classes loaded by it from the application classloader. In the HRManager example, the client is no longer able to cache a direct reference to the bean instance when using the Microcontainer controller. T o see this behavior, start the client using the run.sh command, then try to deploy the service. A java.lang.NoClassDefFoundError exception is thrown and the application exits. In this scenario, you must use the bus to access the service indirectly and provide access to any classes shared by the client in the application classpath. In this example, the affected classes are Address, Em ployee, and SalaryStrategy. 38 Chapitre 5. Ajout de comportement AOP Chapitre 5. Ajout de comportement AOP L'OOP (Object Oriented Programming / Programmation Orientée Objet) contient de nombreuses techniques utiles pour le développement de logiciels, y compris l'encapsulation, l'héritage et le polymorphisme. T outefois, elle ne résout pas le problème de la logique souvent répétée dans différentes classes, par exemple, la logique transactionnelle, d'enregistrement, ou de sécurité qui sont traditionnellement codées en dur dans chaque classe. Ce type de logique est appelé cross-cutting concern (préoccupations intersectorielles). Aspect Oriented Programming (AOP) permet aux cross-cutting concerns d'être appliquées aux classes après qu'elles aient été compilées. Cela permet au code source de demeurer départi de la logique qui n'est pas essentielle à l'objet principal de la classe et qui simplifie la maintenance. La méthode dépend de la mise en œuvre de l'AOP. Si une classe implémente une interface, chaque appel de méthode à une instance de la classe passe d'abord via un proxy. Ce proxy implémente la même interface, ajoutant le comportement requis. Alternativement, si une interface n'est pas utilisée, alors le bytecode Java de la classe compilée sera modifié : les méthodes originales seront renommées et remplacées par des méthodes qui implémentent la logique transversale. Ces nouvelles méthodes appellent ensuite les méthodes originales après que la logique transversale ait été exécutée. Une autre méthode pour obtenir le même résultat consiste à modifier le bytecode pour créer une sous-classe de la classe d'origine qui remplace ses méthodes. Les méthodes substituées exécutent alors la logique transversale avant d'appeler les méthodes correspondantes de la super classe. JBoss AOP est une structure pour l'AOP. En l'utilisant, vous pouvez créer des cross-cutting concerns à l'aide de méthodes et classes java classiques. Dans la terminologie de l'AOP chaque préoccupation (concern) est représentée par un aspect que vous pouvez implémenter à l'aide d'un simple POJO. Ce comportement est fourni par des méthodes qui se trouvent à l'intérieur de l'aspect, et qui s'appellent des advices (conseils). Ces conseils suivent certaines règles de paramétrage et retournent les types et exceptions qu'ils soulèvent. Sur la base de cette structure, vous pouvez utiliser des notions conventionnelles orientées-objet comme l'héritage, l'encapsulation et la composition pour faciliter la maintenance de vos cross-cutting concerns. Les aspects sont appliquées au code à l'aide d'un langage d'expression qui vous permet d'indiquer les constructeurs, les méthodes et les même les champs à cibler. Vous pouvez rapidement modifier le comportement des classes multiples en éditant un fichier de configuration. Ce chapitre contient des exemples qui démontrent comment utiliser JBoss AOP avec le Microcontainer pour créer et pour appliquer un aspect d'audit du Service des ressources humaines. Le code d'audit devra se situer dans la classe HRManager, mais il encombrerait la classe avec le code qui n'est pas central et compliquerait la maintenance. Le design de l'aspect fournit également une certaine modularité, ce qui faciliters les audits de classe à venir, si la portée du projet venait à changer. AOP peut également être utilisé pour faire appliquer des comportement supplémentaires en cours de déploiement. Cet exemple va créer et relier un proxy à une instance de bean dans un service de base JNDI, lui permettant ainsi d'accéder à une recherche de JNDI plutôt qu'à un contrôleur de Microcontainer. 5.1. Créer un aspect Le répertoire exam ples/User_Guide/gettingStarted/auditAspect contient tous les fichiers utiles pour créer l'aspect. pom .xm l src/m ain/java/org/jboss/exam ple/aspect/AuditAspect.java 39 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 5.1. Exemple de POJO public class AuditAspect { private String logDir; private BufferedWriter out; public AuditAspect() { logDir = System.getProperty("user.dir") + "/log"; File directory = new File(logDir); if (!directory.exists()) { directory.mkdir(); } } public Object audit(ConstructorInvocation inv) throws Throwable { SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyy-kkmmss"); Calendar now = Calendar.getInstance(); String filename = "auditLog-" + formatter.format(now.getTime()); File auditLog = new File(logDir + "/" + filename); auditLog.createNewFile(); out = new BufferedWriter(new FileWriter(auditLog)); return inv.invokeNext(); } public Object audit(MethodInvocation inv) throws Throwable { String name = inv.getMethod().getName(); Object[] args = inv.getArguments(); Object retVal = inv.invokeNext(); StringBuffer buffer = new StringBuffer(); for (int i=0; i < args.length; i++) { if (i > 0) { buffer.append(", "); } buffer.append(args[i].toString()); } if (out != null) { out.write("Method: " + name); if (buffer.length() > 0) { out.write(" Args: " + buffer.toString()); } if (retVal != null) { out.write(" Return: " + retVal.toString()); } out.write("\n"); out.flush(); } return retVal; } } 40 Chapitre 5. Ajout de comportement AOP Procédure 5.1. Créer le POJO 1. Le constructeur contrôle la présence d'un répertoire log dans le répertoire de travail en cours, et en crée un s'il n'existe pas. 2. Ensuite, on définit un conseil. Ce conseil est appelé à chaque fois que le constructeur de la classe cible est appelé. Cela crée un nouveau fichier de journalisation dans le répertoire log pour enregistrer les appels de méthode faits sur diverses instances de la classe cible dans des fichiers séparés. 3. Finalement, on définit un autre conseil. Ce conseil s'applique à tout appel de méthode fait sur la classe cible. Le nom de la méthode et les arguments sont stockés, ainsi que la valeur de retour. Cette information est utilisée pour construire une archive d'audit et l'inscrire dans le fichier de journalisation courant. Chaque conseil appelle inv.invokeNext(), qui enchaîne les conseils ensemble plus d'un cross-cutting concern a été appliqué, ou pour appeler la méthode/le construteur de la cible. Note Chaque conseil est utilisé avec une méthode qui prend un objet d'invocation comme paramètre, lance T hrowable et renvoie Object. Au moment de le leur conception, vous ne savez pas à quel constructeur ou méthode ces conseils vont s'appliquer, donc rendez les aussi généraux que possible. Pour compiler la classe et créer un fichier auditAspect.jar qui puissent être utilisé par d'autres exemples, taper m vn install à partir du répertoire auditAspect. 5.2. Configurer le Microcontainer pour AOP Avant d'appliquer l'aspect audit au service HR (RU / Ressources humaines), vous devrez ajouter un certain nombre de JAR au chemin de classe d'extension. Ils se trouvent dans le sous-répertoire lib de la distribution client-aop qui se trouve dans le répertoire exam ples/User_Guide/gettingStarted/com m andLineClient/target/client-aop.dir : 41 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 5.2. Listing du répertoire examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-`-|-`-`-- client-1.0.0.jar jboss-beans.xml lib auditAspect-1.0.0.jar concurrent-1.3.4.jar humanResourcesService-1.0.0.jar javassist-3.6.0.GA.jar jboss-aop-2.0.0.beta1.jar jboss-aop-mc-int-2.0.0.Beta6.jar jboss-common-core-2.0.4.GA.jar jboss-common-core-2.2.1.GA.jar jboss-common-logging-log4j-2.0.4.GA.jar jboss-common-logging-spi-2.0.4.GA.jar jboss-container-2.0.0.Beta6.jar jboss-dependency-2.0.0.Beta6.jar jboss-kernel-2.0.0.Beta6.jar jbossxb-2.0.0.CR4.jar log4j-1.2.14.jar trove-2.1.1.jar xercesImpl-2.7.1.jar log auditLog-18062010-122537 run.sh T out d'abord, lib/auditAspect-1.0.0.jar est nécessaire pour créer une instance de l'aspect au moment de l'exécution, afin d'exécuter la logique. Ensuite le fichier jar pour JBoss AOP (jboss-aop.jar), avec ses dépendances javassist et trove, ajoute la fonctionnalité AOP. Enfin, le jar jboss-aop-mc-int est nécessaire parce qu'il contient une définition de schéma XML qui permet de définir les aspects à l'intérieur d'un descripteur de déploiement XML. Il contient également un code d'intégration pour créer des dépendances entre les beans normaux et beans aspect au sein du Microcontainer, vous permettant d'ajouter un comportement durant les phases de déploiement et d'annulation de déploiement. Comme vous utilisez Maven2 pour assembler la distribution client-aop, vous devez ajouter ces fichiers JAR en déclarant les dépendances qui conviennent dans votre fichier pom .xm l et en créant un descripteur d'assembly valide. Vous trouverez un extrait de pom .xm l dans Exemple 5.3, « Example d'extrait pom .xm l pour AOP ». 42 Chapitre 5. Ajout de comportement AOP Exemple 5.3. Example d'extrait pom .xm l pour AOP <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>jboss-oap</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>javassist</artifactId> <version>3.6.0.GA</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>trove</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>jboss-aop-mc-int</artifactId> <version>2.0.0.Beta6</version> </dependency> 5.3. Appliquer un Aspect Maintenant que vous avez une distribution valide qui contient tout ce dont vous avez besoin, vous pouvez configurer jboss-beans.xm l pour appliquer l'aspect audit. Il se situe dans exam ples/User_Guide/gettingStarted/com m andLineClient/target/client-aop.dir. <?xml version="1.0" encoding="UTF-8"?> <deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd" xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <aop:aspect xmlns:aop="urn:jboss:aop-beans:1.0" name="AuditAspect" class="org.jboss.example.aspect.AuditAspect" method="audit" pointcut="execution(public org.jboss.example.service.HRManager->new(..)) OR execution(public * org.jboss.example.service.HRManager->*(..))"> </aop:aspect> ... </deployment> 43 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Procédure 5.2. Explication du code d'application d'Aspect 1. Avant d'appliquer votre aspect à une classe, vous aurez besoin de créer une instance de org.jboss.aop.AspectManager en utilisant un élément <bean>. On utilise ici une méthode d'usine au lieu d'utiliser un constructeur conventionnel, puisqu'on a besoin d'une seule instance d'AspectManager dans la JVM en cours d'exécution. 2. Ensuite, une instance de l'aspect AuditAspect est créée, en utilisant l'élément <aop:aspect>. Cela ressemble à l'élément <bean> puisqu'elle a des attributs name et class qui sont utilisés de la même façon. Aussi, elle possède les attributs method et pointcut que vous pouvez utiliser pour appliquer ou pour relier un conseil à des constructeurs ou à des méthodes dans d'autres classes. Ces attributs relient le conseil audit à tous les constructeurs ou méthodes de la classe HRManager. Seule la méthode audit a besoin d'être spécifiée, puisqu'elle n'a pas été surchargée par différents paramètres dans la classe AuditAspect. En cours d'exécution, JBoss AOP sait quoi sélectionner, suivant qu'il s'agisse d'un constructeur ou d'un invocation de méthode. Cette configuration supplémentaire est tout ce dont vous avez besoin pour pouvoir faire appliquer l'aspect audit en cours d'exécution, ajoutant ainsi un comportement au service Human Resources. Vous pouvez tester en exécutant le client par le script run.sh. Quand le bean AuditAspect est créé par le Microcontainer, un répertoire log est créé au démarrage en même temps que le répertoire lib. Chaque déploiement du service de ressources humaines fait apparaître un nouveau fichier de journalisation dans le répertoire log. Le fichier de journalisation contient un enregistrement de tout appel du client au service. Il est nommé quelque chose du style auditLog-28112007-163902, et contient des sorties du style Exemple 5.4, « Exemple de sortie le Log AOP ». Exemple 5.4 . Exemple de sortie le Log AOP Method: getEmployees Return: [] Method: addEmployee Args: (Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860) Return: true Method: getSalary Args: (Santa Claus, null - Birth date unknown) Return: 10000 Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)] Method: isHiringFreeze Return: false Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)] Method: getSalaryStrategy Pour retirer le comportement d'audit, retirer les fragments XML concernés du descripteur de déploiement, et redémarrer l'application. Avertissement L'ordre de déploiement est important. Chaque aspect doit être déclaré spécifiquement pour les beans auxquels il s'applique, pour le le Microcontainer puisse les déployer dans cet ordre. C'est parce que le Microcontainer a sans doute besoin de modifier le bytecode de la classe de bean normale pour ajouter la logique transversale, avant de créer une instance et stocker en créer une référence dans le contrôleur. Cela n'est pas possible si une instance normale de bean a déjà été créée. 44 Chapitre 5. Ajout de comportement AOP 5.4. Lifecycle Callbacks En plus d'appliquer des aspects à des beans que l'on instancie par le Microcontainer, nous pouvons également ajouter un comportement au cours du processus de déploiement ou de retrait du déploiement. Comme noté dans Section 4.3, « Accès direct », un bean passe par différents états en cours de déploiement. Ces états incluent : NOT _INST ALLED le descripteur de déploiement qui contient le bean a été traité, ainsi que toute annotation qui se trouve sur le bean. DESCRIBED toute dépendance créée par AOP ont été ajoutées au bean, et les annotations personnalisées ont été traitées. INST ANT IAT ED une instance de bean a été créée. CONFIGURED les propriétés ont été injectées dans le bean, ainsi que toute référence aux autres beans. CREAT E la méthode create, à condition qu'elle ait été définie dans le bean, est appelée. ST ART la méthode start, à condition qu'elle ait été définie dans le bean, est appelée. INST ALLED toutes les actions d'installation personnalisées qui ont été définies dans le descripteur de déploiement ont été exécutées et le bean est prêt à l'accès. Important Les états CREAT E et ST ART sont inclus pour des questions d'héritage. Cela permet aux services qui étaient implémentés comme MBeans dans les versions antérieures d'Enterprise Platform de fonctionner correctement quand ils sont implémentés en tant que beans dans Enterprise Platform 5.1. Si vous ne définissez pas de méthode create/start correspondante dans votre bean, il passera directement à travers ces états. Ces états représentent le cycle de vie du bean. Vous pouvez définir un certain nombre de callbacks qui peuvent s'appliquer à n'importe quel point, en utilisant un groupe d'éléments <aop> séparé : <aop:lifecycle-describe> s'applique quand on entre/sort de l'état DÉCRIT 45 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer <aop:lifecycle-instantiate> s'applique quand on entre/sort de l'état INST ANCIÉ <aop:lifecycle-configure> s'applique quand on entre/sort de l'état CONFIGURÉ <aop:lifecycle-create> s'applique quand on entre/sort de l'état CRÉER <aop:lifecycle-start> s'applique quand on entre/sort de l'état ST ART <aop:lifecycle-install> s'applique quand on entre/sort de l'état INST ALLÉ T out comme les éléments <bean> et <aop:aspect>, les éléments <aop:lifecycle-> contiennent des attributs name et class. Le Microcontainer utilise ces attributs pour créer une instance de la classe callback, en la nommant pour qu'elle puisse être utilisée au fur et à mesure que les beans entrent ou sortent d'un état particulier en cours de déploiement ou en cours de retrait de déploiement. Vous pourrez spécifier quels beans sont affectés par le callack en utilisant l'attribut de classe, comme le montre Exemple 5.5, « Utilisation de l'attribut classes ». Exemple 5.5. Utilisation de l'attribut classes <aop:lifecycle-install xmlns:aop="urn:jboss:aop-beans:1.0" name="InstallAdvice" class="org.jboss.test.microcontainer.support.LifecycleCallback" classes="@org.jboss.test.microcontainer.support.Install"> </aop:lifecycle-install> Ce code précise qu'une logique supplémentaire de la classe lifecycleCallback a été appliquée à toute classe de bean annotée par @org.jboss.test.microcontainer.support.Install avant d'entrer ou de quitter l'état INST ALLED. Pour que la classe de callback fonctionne, elle doit contenir les méthodes install et uninstall qui prennent ControllerContext comme paramètre, comme dans Exemple 5.6, « Méthodes d'installation et de désinstallation ». 46 Chapitre 5. Ajout de comportement AOP Exemple 5.6. Méthodes d'installation et de désinstallation import org.jboss.dependency.spi.ControllerContext; public class LifecycleCallback { public void install(ControllerContext ctx) { System.out.println("Bean " + ctx.getName() + " is being installed"; } public void uninstall(ControllerContext ctx) { System.out.println("Bean " + ctx.getName() + " is being uninstalled"; } } La méthode install est appelée au cours du déploiement du bean, et la méthode uninstall est appelée en cours de retrait du déploiement. Note Bien que les callbacks rajoutent du comportement aux processus de déploiement et de retrait de déploiement, AOP n'est pas utilisé dans ces cas. La fonctionnalité pointcut expression de JBoss AOP est utilisée pour déterminer à quelles classes de bean les comportements s'appliquent. 5.5. Ajout de Recherche de service (Look-ups) par JNDI Jusqu'à présent, vous avez utilisé le Microcontainer pour chercher des références d'instances de beans qui représentent des services. Ce n'est pas l'idéal, parce que vous avez besoin d'une référence au noyau du Micro container avant d'accéder du contrôleur. Vous pourrez voir cela dans Exemple 5.7, « Rechercher des références de Beans ». 47 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 5.7. Rechercher des références de Beans private private private private private HRManager manager; EmbeddedBootstrap bootstrap; Kernel kernel; KernelController controller; final static String HRSERVICE = "HRService"; ... // Start JBoss Microcontainer bootstrap = new EmbeddedBootstrap(); bootstrap.run(); kernel = bootstrap.getKernel(); controller = kernel.getController(); ... ControllerContext context = controller.getInstalledContext(HRSERVICE); if (context != null) { manager = (HRManager) context.getTarget(); } Donner des références de noyau à tous les clients qui recherchent un service présente un risque de sécurité, parce que cela donne accès général à la configuration du Microcontainer. Pour une meilleure sécurité, appliquer le modèle ServiceLocator et utiliser une classe pour pouvoir effectuer les recherches pour le compte des clients. Mieux encore, passez les références de beans, ainsi que leur nom, au ServiceLocator au moment du déploiement, en utilisant un callback de cycle de vie. Dans un tel scénario, le ServiceLocator peut les chercher sans être au courant du Microcontainer. Le retrait du déploiement retirerait par conséquence les références de bean du ServiceLocator pour éviter toute recherche supplémentaire. Il n'est pas difficile d'écrire votre propre implémentation de ServiceLocator. En intégrer une comme JBoss Naming Service (JBoss NS) est même plus rapide, et a comme avantage supplémentaire d'être en accord avec la norme JNDI (Java Naming and DIrectory Interface). JNDI permet aux clients d'accéder à plusieurs, peut-être multiples services de nommage en utilisant un API en commun. Procédure 5.3. Rédiger votre propre implémentation de ServiceLocator 1. T out d'abord, créer une instance de JBoss NS en utilisant le Microcontainer. 2. Puis, ajouter un callback de cycle de vie pour procéder à la liaison ou séparation des références de bean pendant le déploiement ou son retrait. 3. Marquez les classes de bean pour lesquelles vous souhaitez relier des références, en utilisant des annotations. 4. Maintenant, vous pouvez localiser les beans en cours d'exécution grâce à l'expression pointcut raccourcie, comme montré plus haut. 48 Partie II. Concepts avancés de Microcontainer Partie II. Concepts avancés de Microcontainer Cette section couvre des concepts avancés, et montrent quelques fonctionnalités intéressantes du Microcontainer. On assume que les exemples de code qui se trouvent dans le reste du guide sont des exemples incomplets, et que c'est la responsabilité du programmeur de les extrapoler ou de les étendre, suivant les besoins. 49 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Chapitre 6. Modèles de composants JBoss Microcontainer fonctionne avec plusieurs modèles de composants POJO connus. Les composants sont des programmes de logiciels réutilisables que vous pouvez développer et assembler facilement pour créer des applications sophistiquées. L'intégration effective de ces modèles de composants était le but principal du Microcontainer. JMX, Spring et Guice sont des modèles de composants populaires utilisables avec Microcontainer. 6.1. Interactions permises avec les modèles de composants Avant de discuter l'interaction avec certains des modèles de composants les plus populaires, il faut bien comprendre quels types d'interaction sont permis. JMX MBeans représentent un exemple de modèle de composant. Leurs interactions s'étendent aux opérations MBean, le référencement des attributs, la configuration des attributs, et la déclaration des dépendances explicites entres les MBeans nommés. Les interactions et comportements par défaut de Microcontainer sont les mêmes que n'importe quel autre conteneur Inversion of Control (IoC) et ressemblent à la fonctionnalité fournie par MBeans, y compris les invocations de méthodes pleines pour les opérations, les setters/getters pour les attributs et dépendances explicites. 6.2. Un bean sans dépendances Exemple 6.1, « Descripteur de déploiement pour un POJO simple. » montre un descripteur de déploiement pour un POJO simple sans dépendance. Il s'agit du point de départ pour intégrer le Microcontainer avec Spring ou Guice. Exemple 6.1. Descripteur de déploiement pour un POJO simple. <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="PlainPojo" class="org.jboss.demos.models.plain.Pojo"/> <beanfactory name="PojoFactory" class="org.jboss.demos.models.plain.Pojo"> <property name="factoryClass">org.jboss.demos.models.plain.PojoFactory</property> </beanfactory> </deployment> 6.3. Utilisation de Microcontainer avec Spring 50 Chapitre 6. Modèles de composants Exemple 6.2. Descripteur avec Support Spring <beans xmlns="urn:jboss:spring-beans:2.0"> <!-- Adding @Spring annotation handler --> <bean id="SpringAnnotationPlugin" class="org.jboss.spring.annotations.SpringBeanAnnotationPlugin" /> <bean id="SpringPojo" class="org.jboss.demos.models.spring.Pojo"/> </beans> L'espace-nom du fichier est différent du fichier de bean du Microcontainer simple. L'espace-nom de urn:jboss:spring-beans:2.0 pointe vers votre version du port de schéma Spring, qui décrit le style Spring de votre bean. Le Microcontainer, et non pas la notion d'usine de bean de Spring, déploie le bean. Exemple 6.3. Utilisation de Spring avec le Microcontainer public class Pojo extends AbstractPojo implements BeanNameAware { private String beanName; public void setBeanName(String name) { beanName = name; } public String getBeanName() { return beanName; } public void start() { if ("SpringPojo".equals(getBeanName()) == false) throw new IllegalArgumentException("Name doesn't match: " + getBeanName()); } } Malgré que le bean SpringPojo ait une dépendance dans la bibliothèque de Spring causée par l'implémentation de l'interface BeanNameAware, son seul but est d'exposer le comportement callback de Spring. 51 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Le focus de Guice est de faire correspondre les types. Les beans Guice sont générés et configurés en utilisant des modules. Exemple 6.4 . Le descripteur de déploiement d'intégration de Guice dans le Microcontainer <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="GuicePlugin" class="org.jboss.guice.spi.GuiceKernelRegistryEntryPlugin"> <constructor> <parameter> <array elementClass="com.google.inject.Module"> <bean class="org.jboss.demos.models.guice.PojoModule"/> </array> </parameter> </constructor> </bean> </deployment> Les parties importantes à observer dans ce fichier sont PojoModule et GuiceKernelRegistryEntryPlugin. PojoModule configure vos beans, comme dans Exemple 6.5, « Configurer les beans pour Guice ». GuiceKernelRegistryEntryPlugin fournit l'intégration dans Microcontainer, comme le montre Exemple 6.6, « Intégration de Guice dans Microconteneur ». 52 Chapitre 6. Modèles de composants Exemple 6.6. Intégration de Guice dans Microconteneur public class GuiceKernelRegistryEntryPlugin implements KernelRegistryPlugin { private Injector injector; public GuiceKernelRegistryEntryPlugin(Module... modules) { injector = Guice.createInjector(modules); } public void destroy() { injector = null; } public KernelRegistryEntry getEntry(Object name) { KernelRegistryEntry entry = null; try { if (name instanceof Class<?>) { Class<?> clazz = (Class<?>)name; entry = new AbstractKernelRegistryEntry(name, injector.getInstance(clazz)); } else if (name instanceof Key) { Key<?> key = (Key<?>)name; entry = new AbstractKernelRegistryEntry(name, injector.getInstance(key)); } } catch (Exception ignored) { } return entry; } } Note Injector est créé à partir de la classe Modules, puis il recherche les beans qui correspondent. Voir Section 6.5, « Mbeans hérités, et comment mixer différents modèles de composants » pour obtenir des informations pour pouvoir déclarer et utiliser des MBeans hérités. 6.5. Mbeans hérités, et comment mixer différents modèles de composants La façon la plus simple de mixer différents modèles de composants est expliquée dans Exemple 6.7, 53 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer « Injecter un POJO dans un MBean ». Exemple 6.7. Injecter un POJO dans un MBean <server> <mbean code="org.jboss.demos.models.mbeans.Pojo" name="jboss.demos:service=pojo"> <attribute name="OtherPojo"><inject bean="PlainPojo"/></attribute> </mbean> </server> Pour effectuer un déploiement MBeans via Microcontainer, vous devez rédiger un nouveau gestionnaire pour le modèle du composant. Voir system -jm x-beans.xm l pour obtenir davantage d'informations. Le code de ce fichier réside dans le code source du serveur de JBoss Application : le sous-projet systemjmx. 6.6. Exposer les POJO en tant que MBeans Exemple 6.8. Exposer un POJO existant en tant que MBean <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AnnotatedJMXPojo" class="org.jboss.demos.models.jmx.AtJmxPojo"/> <bean name="XmlJMXPojo" class="org.jboss.demos.models.mbeans.Pojo"> <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(exposedInterface=org.jb oss.demos.models.mbeans.PojoMBean.class, registerDirectly=true)</annotation> </bean> <bean name="ExposedPojo" class="org.jboss.demos.models.jmx.Pojo"/> <bean name="AnnotatedExposePojo" class="org.jboss.demos.models.jmx.ExposePojo"> <constructor> <parameter><inject bean="ExposedPojo"/></parameter> </constructor> </bean> </deployment> Ce descripteur expose un POJO existant en tant que MBean, et l'enregistre dans un serveur MBean. Pour exposer un POJO en tant que MBean, terminez-le par l'annotation @JMX, en assumant que vous ayez importé org.jboss.aop.m icrocontainer.aspects.jm x.JMX. Le bean peut alors être exposé directement, ou bien dans sa propriété. 54 Chapitre 6. Modèles de composants Exemple 6.9. Exposer un POJO en tant que MBean en utilisant sa propriété <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="XMLLoginConfig" class="org.jboss.demos.models.old.XMLLoginConfig"/> <bean name="SecurityConfig" class="org.jboss.demos.models.old.SecurityConfig"> <property name="defaultLoginConfig"><inject bean="XMLLoginConfig"/></property> </bean> <bean name="SecurityChecker" class="org.jboss.demos.models.old.Checker"> <property name="loginConfig"><inject bean="jboss.security:service=XMLLoginConfig"/></property> <property name="securityConfig"><inject bean="jboss.security:service=SecurityConfig"/></property> </bean> </deployment> Vous pouvez utiliser n'importe quel type de recherche d'injection, soit en recherchant un POJO simple, ou en obtenant un gestionnaire pour un MBean à partir du serveur MBean. Une des options d'injection consiste à utiliser injection type, parfois nommée autowiring, comme vous verrez dans Exemple 6.10, « Autowiring ». Exemple 6.10. Autowiring <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="FromGuice" class="org.jboss.demos.models.plain.FromGuice"> <constructor><parameter><inject bean="PlainPojo"/></parameter></constructor> <property name="guicePojo"><inject/></property> </bean> <bean name="AllPojos" class="org.jboss.demos.models.plain.AllPojos"> <property name="directMBean"><inject bean="jboss.demos:service=pojo"/></property> <property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property> <property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property> </bean> </deployment> Le bean FromGuice injecte le bean Guice par la correponsdance de type, avec PlainPojo injecté par 55 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer une injection de nom commun. Maintenant, vous pouvez tester la liaison Guice fonctionne comme prévu dans Exemple 6.11, « T ester la fonctionnalité Guice ». Exemple 6.11. T ester la fonctionnalité Guice public class FromGuice { private IPojo plainPojo; private org.jboss.demos.models.guice.Pojo guicePojo; public FromGuice(IPojo plainPojo) { this.plainPojo = plainPojo; } public void setGuicePojo(org.jboss.demos.models.guice.Pojo guicePojo) { this.guicePojo = guicePojo; } public void start() { f (plainPojo != guicePojo.getMcPojo()) throw new IllegalArgumentException("Pojos are not the same: " + plainPojo + "!=" + guicePojo.getMcPojo()); } } Exemple 6.11, « T ester la fonctionnalité Guice » ne fournit qu'un modèle de composant alias. L'alias est une fonctionnalité triviale, mais nécessaire. Il doit être présenté comme un nouveau modèle de composant à l'intérieur du Microcontainer, pour pouvoir l'implémenter en tant que véritable dépendance. Les détails d'implémentation sont disponibles dans Exemple 6.12, « Code source du AbstractController ». Exemple 6.12. Code source du AbstractController <deployment xmlns="urn:jboss:bean-deployer:2.0"> <alias name="SpringPojo">springPojo</alias> </deployment> Le descripteur mappe le nom SpringPojo à l'alias springPojo. L'avantage des alias en tant que véritables modèles de composants, c'est que le timing de déploiement de beans devient moins important. L'alias patiente dans un état non-installé jusqu'à ce qu'un véritable bean le déclenche. 56 Chapitre 7. Injection de dépendances avancées et IoC Chapitre 7. Injection de dépendances avancées et IoC Aujourd'hui, Dependency injection (DI), qui s'appelle également Inversion of Control (IoC), est au coeur de nombreux frameworks qui embrassent la notion d'un modèle de composant ou de conteneur. Nous avons abordé les modèles de composants dans un chapitre précédent. Le noyau JBoss JMX, précurseur du Microcontainer, ne fournit qu'un support léger DI/IoC, surtout en raison des limitations pour accéder à MBeans par le serveur MBeans. Cependant, avec le nouveau modèle de composant basé-POJO, on trouve plusieurs fonctionnalités à la fois nouvelles et intéressantes. Ce chapitre vous montre comment appliquer différents concepts DI à l'aide du Microcontainer JBoss. Ces concepts seront exprimés par le code XML, mais vous pourrez appliquer la plupart de ces fonctionnalités en utilisant des annotations. 7.1. Usine de valeurs Une usine de valeurs est un bean qui possède une ou plusieurs méthodes destinées à générer des valeurs. Voir Exemple 7.1, « Usine de valeurs ». 57 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.1. Usine de valeurs <bean name="Binding" class="org.jboss.demos.ioc.vf.PortBindingManager"> <constructor> <parameter> <map keyClass="java.lang.String" valueClass="java.lang.Integer"> <entry> <key>http</key> <value>80</value> </entry> <entry> <key>ssh</key> <value>22</value> </entry> </map> </parameter> </constructor> </bean> <bean name="PortsConfig" class="org.jboss.demos.ioc.vf.PortsConfig"> <property name="http"><value-factory bean="Binding" method="getPort" parameter="http"/></property> <property name="ssh"><value-factory bean="Binding" method="getPort" parameter="ssh"/></property> <property name="ftp"> <value-factory bean="Binding" method="getPort"> <parameter>ftp</parameter> <parameter>21</parameter> </value-factory> </property> <property name="mail"> <value-factory bean="Binding" method="getPort"> <parameter>mail</parameter> <parameter>25</parameter> </value-factory> </property> </bean> Exemple 7.2, « PortsConfig » montre comment le bean PortsConfig utilise le bean de Liaison pour obtenir ses valeurs par l'invocation de méthode getPort. 58 Chapitre 7. Injection de dépendances avancées et IoC Exemple 7.2. PortsConfig public class PortBindingManager { private Map<String, Integer> bindings; public PortBindingManager(Map<String, Integer> bindings) { this.bindings = bindings; } public Integer getPort(String key) { return getPort(key, null); } public Integer getPort(String key, Integer defaultValue) { if (bindings == null) return defaultValue; Integer value = bindings.get(key); if (value != null) return value; if (defaultValue != null) bindings.put(key, defaultValue); return defaultValue; } } 7.2. Callbacks Le descripteur qui s'affiche dans Exemple 7.3, « Callbacks à collecter et beans de filtrage » vous permet de collecter tous les beans d'un certain type, et même de limiter le nombre de beans correspondants. En conjonction au descripteur Exemple 7.3, « Callbacks à collecter et beans de filtrage », le code Java affiché dans Exemple 7.4, « Un analyseur pour collecter tous les éditeurs » montre un Parser (analyseur ou lecteur) qui collecte tous les Editor (éditeur) 59 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.3. Callbacks à collecter et beans de filtrage <bean name="checker" class="org.jboss.demos.ioc.callback.Checker"> <constructor> <parameter> <value-factory bean="parser" method="parse"> <parameter> <array elementClass="java.lang.Object"> <value>http://www.jboss.org</value> <value>SI</value> <value>3.14</value> <value>42</value> </array> </parameter> </value-factory> </parameter> </constructor> </bean> <bean name="editorA" class="org.jboss.demos.ioc.callback.DoubleEditor"/> <bean name="editorB" class="org.jboss.demos.ioc.callback.LocaleEditor"/> <bean name="parser" class="org.jboss.demos.ioc.callback.Parser"> <incallback method="addEditor" cardinality="4..n"/> <uncallback method="removeEditor"/> </bean> <bean name="editorC" class="org.jboss.demos.ioc.callback.LongEditor"/> <bean name="editorD" class="org.jboss.demos.ioc.callback.URLEditor"/> Exemple 7.4 . Un analyseur pour collecter tous les éditeurs public class Parser { private Set<Editor> editors = new HashSet<Editor>(); ... public void addEditor(Editor editor) { editors.add(editor); } public void removeEditor(Editor editor) { editors.remove(editor); } } Notez que incallback et uncallback utilisent le nom de la méthode pour la correspondance. 60 Chapitre 7. Injection de dépendances avancées et IoC <incallback method="addEditor" cardinality="4..n"/> <uncallback method="removeEditor"/> Une limite contrôle le nombre d'éditeurs qui peuvent entraîner le bean à progresser vers un état Configuré : cardinality=4 ..n/> Finalement, Checker est créé et contrôle l'analyseur. Cela est illustré dans Exemple 7.5, « Checker d'analyseur. ». Exemple 7.5. Checker d'analyseur. public void create() throws Throwable { Set<String> strings = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); for (Object element : elements) strings.add(element.toString()); if (expected.equals(strings) == false) throw new IllegalArgumentException("Illegal expected set: " + expected + "!=" + strings); } 7.3. Mode d'accès de bean Avec BeanAccessMode par défaut, les champ d'un bean ne sont pas inspectés. Cependant, si cous précisez un nouveau BeanAccessMode, les champs seront accessibles puisqu'ils font partie des propriétés du bean. Voir Exemple 7.6, « Définitions possibles de BeanAccessMode », Exemple 7.7, « Configurer le BeanAccessMode », et Exemple 7.8, « La classe FieldsBean » pour une implémentation. Exemple 7.6. Définitions possibles de BeanAccessMode public enum BeanAccessMode { STANDARD(BeanInfoCreator.STANDARD), // Getters and Setters FIELDS(BeanInfoCreator.FIELDS), // Getters/Setters and fields without getters and setters ALL(BeanInfoCreator.ALL); // As above but with non public fields included } Ici, une valeur de String est allouée à un champ de String privé. 61 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.7. Configurer le BeanAccessMode <bean name="FieldsBean" class="org.jboss.demos.ioc.access.FieldsBean" accessmode="ALL"> <property name="string">InternalString</property> </bean> Exemple 7.8. La classe FieldsBean public class FieldsBean { private String string; public void start() { if (string == null) throw new IllegalArgumentException("Strings should be set!"); } } 7.4. Bean Alias Chaque bean peut contenir un certain nombre d'alias. Comme les noms de composants de Microcontainer sont traités comme des objets, le type d'alias n'est pas limité. Le remplacement d'une propriété de système n'existe pas par défaut, vous devez poser le drapeau de remplacement explicitement, comme le montre Exemple 7.9, « Un simple alias de bean ». Exemple 7.9. Un simple alias de bean <bean name="SimpleName" class="java.lang.Object"> <alias>SimpleAlias</alias> <alias replace="true">${some.system.property}</alias> <alias class="java.lang.Integer">12345</alias> <alias><javabean xmlns="urn:jboss:javabean:2.0" class="org.jboss.demos.bootstrap.Main"/></alias> </bean> 7.5. Support d'annotations XML (ou Métadonnées) Le support AOP est une fonctionnalité principale de JBoss Microcontainer. Vous pouvez utiliser les 62 Chapitre 7. Injection de dépendances avancées et IoC aspects AOP ou les simples beans dans n'importe quelle combinaison. Exemple 7.10, « Intercepter une méthode basée Annotation » tente d'intercepter une invocation de méthode sur la base d'une annotation. L'annotation peut provenir de n'importe quel endroit. Il peut s'agir d'une annotation de classe ou d'une annotation ajoutée à travers la configuration xml. Exemple 7.10. Intercepter une méthode basée Annotation <interceptor xmlns="urn:jboss:aop-beans:1.0" name="StopWatchInterceptor" class="org.jboss.demos.ioc.annotations.StopWatchInterceptor"/> <bind xmlns="urn:jboss:aop-beans:1.0" pointcut="execution(* @org.jboss.demos.ioc.annotations.StopWatchLog->*(..)) OR execution(* *>@org.jboss.demos.ioc.annotations.StopWatchLog(..))"> <interceptor-ref name="StopWatchInterceptor"/> </bind> </interceptor> public class StopWatchInterceptor implements Interceptor { ... public Object invoke(Invocation invocation) throws Throwable { Object target = invocation.getTargetObject(); long time = System.currentTimeMillis(); log.info("Invocation [" + target + "] start: " + time); try { return invocation.invokeNext(); } finally { log.info("Invocation [" + target + "] time: " + (System.currentTimeMillis() time)); } } } Exemple 7.11, « Un exécuteur annoté true class » and Exemple 7.12, « Simple exécuteur avec annotation XML » montre différentes façons d'implémenter les exécuteurs. 63 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.11. Un exécuteur annoté true class <bean name="AnnotatedExecutor" class="org.jboss.demos.ioc.annotations.AnnotatedExecutor"> public class AnnotatedExecutor implements Executor { ... @StopWatchLog // <-- Pointcut match! public void execute() throws Exception { delegate.execute(); } } Exemple 7.12. Simple exécuteur avec annotation XML <bean name="SimpleExecutor" class="org.jboss.demos.ioc.annotations.SimpleExecutor"> <annotation>@org.jboss.demos.ioc.annotations.StopWatchLog</annotation> // <-Pointcut match! </bean> public class SimpleExecutor implements Executor { private static Random random = new Random(); public void execute() throws Exception { Thread.sleep(Math.abs(random.nextLong() % 101)); } } Après avoir ajouté des beans invocateurs exécuteurs, vous pourrez voir les exécuteurs en action pendant l'emploi, en regardant dans les sorties de log telles que Exemple 7.13, « Sortie de Log d'Executeur ». 64 Chapitre 7. Injection de dépendances avancées et IoC Exemple 7.13. Sortie de Log d'Executeur JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] start: 1229345859234 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] time: 31 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] start: 1229345859265 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] time: 47 7.6. Autowire Autowiring ou injection contextuelle, est une fonctionnalité commune des frameworks IoC. Exemple 7.14, « Inclure ou exclure avec Autowiring » vous montre comment utiliser ou exclure des beans d'autowiring. Exemple 7.14 . Inclure ou exclure avec Autowiring <bean name="Square" class="org.jboss.demos.ioc.autowire.Square" autowirecandidate="false"/> <bean name="Circle" class="org.jboss.demos.ioc.autowire.Circle"/> <bean name="ShapeUser" class="org.jboss.demos.ioc.autowire.ShapeUser"> <constructor> <parameter><inject/></parameter> </constructor> </bean> <bean name="ShapeHolder" class="org.jboss.demos.ioc.autowire.ShapeHolder"> <incallback method="addShape"/> <uncallback method="removeShape"/> </bean> <bean name="ShapeChecker" class="org.jboss.demos.ioc.autowire.ShapesChecker"/> Dans les deux cas - ShapeUser et ShapeChecker - seul Circle doit être utilisé, car Square est exclus du contexte de liaison. 7.7. Usine de bean Quand vous souhaitez plus d'une instance pour un bean particulier, vous devez utiliser le modèle d'usine de bean. Le travail du Microcontainer consiste à configurer ou à installer l'usine de beans, comme s,il s'agissait d'un bean simple. ensuite, vous aurez besoin d'invoquer la méthode createBean de l'usine de beans. Par défaut, le Microcontainer crée une instance GenericBeanFactory , mais vous pouvez configurer votre propre usine. La seule limitation est que sa signature et des crochets de configuration ressemblent à ceux de AbstractBeanFactory. 65 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.15. Usine de beans génériques <bean name="Object" class="java.lang.Object"/> <beanfactory name="DefaultPrototype" class="org.jboss.demos.ioc.factory.Prototype"> <property name="value"><inject bean="Object"/></property> </beanfactory> <beanfactory name="EnhancedPrototype" class="org.jboss.demos.ioc.factory.Prototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory"> <property name="value"><inject bean="Object"/></property> </beanfactory> <beanfactory name="ProxiedPrototype" class="org.jboss.demos.ioc.factory.UnmodifiablePrototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory"> <property name="value"><inject bean="Object"/></property> </beanfactory> <bean name="PrototypeCreator" class="org.jboss.demos.ioc.factory.PrototypeCreator"> <property name="default"><inject bean="DefaultPrototype"/></property> <property name="enhanced"><inject bean="EnhancedPrototype"/></property> <property name="proxied"><inject bean="ProxiedPrototype"/></property> </bean> Voir Exemple 7.16, « BeanFactory étendue » pour l'utilisation d'une BeanFactory étendue. 66 Chapitre 7. Injection de dépendances avancées et IoC Exemple 7.16. BeanFactory étendue public class EnhancedBeanFactory extends GenericBeanFactory { public EnhancedBeanFactory(KernelConfigurator configurator) { super(configurator); } public Object createBean() throws Throwable { Object bean = super.createBean(); Class clazz = bean.getClass(); if (clazz.isAnnotationPresent(SetterProxy.class)) { Set<Class> interfaces = new HashSet<Class>(); addInterfaces(clazz, interfaces); return Proxy.newProxyInstance( clazz.getClassLoader(), interfaces.toArray(new Class[interfaces.size()]), new SetterInterceptor(bean) ); } else { return bean; } } protected static void addInterfaces(Class clazz, Set<Class> interfaces) { if (clazz == null) return; interfaces.addAll(Arrays.asList(clazz.getInterfaces())); addInterfaces(clazz.getSuperclass(), interfaces); } private class SetterInterceptor implements InvocationHandler { private Object target; private SetterInterceptor(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.startsWith("set")) throw new IllegalArgumentException("Cannot invoke setters."); return method.invoke(target, args); } } } 67 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer public class PrototypeCreator { ... public void create() throws Throwable { ValueInvoker vi1 = (ValueInvoker)bfDefault.createBean(); vi1.setValue("default"); ValueInvoker vi2 = (ValueInvoker)enhanced.createBean(); vi2.setValue("enhanced"); ValueInvoker vi3 = (ValueInvoker)proxied.createBean(); try { vi3.setValue("default"); throw new Error("Should not be here."); } catch (Exception ignored) { } } 7.8. Constructeur de métadonnées de beans Quand vous utilisez le Microcontainer dans votre code, utiliser BeanMetaDataBuilder pour créer et configurer les métadonnées de votre bean. 68 Chapitre 7. Injection de dépendances avancées et IoC Exemple 7.17. BeanMetaDataBuilder <bean name="BuilderUtil" class="org.jboss.demos.ioc.builder.BuilderUtil"/> <bean name="BuilderExampleHolder" class="org.jboss.demos.ioc.builder.BuilderExampleHolder"> <constructor> <parameter><inject bean="BUExample"/></parameter> </constructor> </bean> En utilisant ce concept, vous n'exposez pas votre code à des détails d'implémentation de Microcontainer. public class BuilderUtil { private KernelController controller; @Constructor public BuilderUtil(@Inject(bean = KernelConstants.KERNEL_CONTROLLER_NAME) KernelController controller) { this.controller = controller; } public void create() throws Throwable { BeanMetaDataBuilder builder = BeanMetaDataBuilder.createBuilder("BUExample", BuilderExample.class.getName()); builder.addStartParameter(Kernel.class.getName(), builder.createInject(KernelConstants.KERNEL_NAME)); controller.install(builder.getBeanMetaData()); } public void destroy() { controller.uninstall("BUExample"); } } 7.9. Personnalisation du chargeur de classes. Dans le Microcontainer, vous pouvez définir un chargeur de classes par bean. Quand vous définissez un chargeur de classes pour tout le déploiement, faîtes attention de ne pas créer une dépendance cyclique -- par exemple, un chargeur de classes qui dépendrait de lui-même. 69 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.18. Définir un chargeur de classe par bean <classloader><inject bean="custom-classloader:0.0.0"/></classloader> <!-- this will be explained in future article --> <classloader name="custom-classloader" xmlns="urn:jboss:classloader:1.0" exportall="NON_EMPTY" import-all="true"/> <bean name="CustomCL" class="org.jboss.demos.ioc.classloader.CustomClassLoader"> <constructor> <parameter><inject bean="custom-classloader:0.0.0"/></parameter> </constructor> <property name="pattern">org\.jboss\.demos\.ioc\..+</property> </bean> <bean name="CB1" class="org.jboss.demos.ioc.classloader.CustomBean"/> <bean name="CB2" class="org.jboss.demos.ioc.classloader.CustomBean"> <classloader><inject bean="CustomCL"/></classloader> </bean> Exemple 7.19, « Personnalisation du test de chargeur de classe » montre un test qui vérifie que le bean CB2 utilise un chargeur de classes personnalisé. qui limite l'étendue des packages chargeables. Exemple 7.19. Personnalisation du test de chargeur de classe public class CustomClassLoader extends ClassLoader { private Pattern pattern; public CustomClassLoader(ClassLoader parent) { super(parent); } public Class<?> loadClass(String name) throws ClassNotFoundException { if (pattern == null || pattern.matcher(name).matches()) return super.loadClass(name); else throw new ClassNotFoundException("Name '" + name + "' doesn't match pattern: " + pattern); } public void setPattern(String regexp) { pattern = Pattern.compile(regexp); } } 7.10. Mode Controller Par défaut, le Microcontainer utilise le mode contrôleur AUT O. Il pousse les beans autant que possible, en fonction des dépendances. Il existe, cependant, deux autres modes : MANUAL et ON_DEMAND. Si le bean est marqué ON_DEMAND, il ne sera pas utilisé ou installé à moins qu'un autre bean en 70 Chapitre 7. Injection de dépendances avancées et IoC dépende explicitement. En mode MANUAL, l'utilsateur du Microcontainer doit pousser le bean en avant et en arrière le long de l'échelle des états. Exemple 7.20. Bean Controller Mode <bean name="OptionalService" class="org.jboss.demos.ioc.mode.OptionalService" mode="On Demand"/> <bean name="OptionalServiceUser" class="org.jboss.demos.ioc.mode.OptionalServiceUser"/> <bean name="ManualService" class="org.jboss.demos.ioc.mode.ManualService" mode="Manual"/> <bean name="ManualServiceUser" class="org.jboss.demos.ioc.mode.ManualServiceUser"> <start> <parameter><inject bean="ManualService" fromContext="context" state="Not Installed"/></parameter> </start> </bean> Note En utilisant l'attribut fromContext de la classe inject, vous pouvez injecter des beans, ainsi que leur représentation de composant de Microcontainer non modifiable. Réviser le code de OptionalServiceUser et de ManualServiceUser sur la façon d'utiliser l'API Microcontainer pour la gestion de bean ON_DEMAND ou MANUAL. 7.11. Cycle Dans un cycle, les beans dépendent parfois les uns des autres. Ainsi, A dépend de B au moment de la constrution, mais B dépend de A pour la méthode setter. Le problème peut être résolu grâce à la séparation du cycle de vie d'état à fine granularité du Microcontainer. 71 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.21. Séparation du cycle de vie de bean <bean name="cycleA" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleB"/></property> </bean> <bean name="cycleB" class="org.jboss.demos.ioc.cycle.CyclePojo"> <constructor><parameter><inject bean="cycleA" state="Instantiated"/></parameter></constructor> </bean> <bean name="cycleC" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleD"/></property> </bean> <bean name="cycleD" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleC" state="Instantiated"/></property> </bean> 7.12. Offre et demande Parfois, comme pour le cas d'une injection, la dépendance entre deux beans n'est peut-être pas apparente. De telles dépendances doivent être présentées clairement, comme le montre Exemple 7.22, « Utilisation d'un code statique ». Exemple 7.22. Utilisation d'un code statique <bean name="TMDemand" class="org.jboss.demos.ioc.demandsupply.TMDemander"> <demand>TM</demand> </bean> <bean name="SimpleTMSupply" class="org.jboss.demos.ioc.demandsupply.SimpleTMSupplyer"> <supply>TM</supply> </bean> 7.13. Installs Au fur et à mesure que les beans évoluent vers des états différents, vous souhaiterez sans doute invoquer certaines méthodes vers d'autres beans ou sur le même bean. Exemple 7.23, « Méthodes d'invocation dans des états différents » vous montre comment Entry invoque les méthodes add et rem oveEntryRepositoryManager pour s'enregistrer et se désenregistrer. 72 Chapitre 7. Injection de dépendances avancées et IoC Exemple 7.23. Méthodes d'invocation dans des états différents <bean name="RepositoryManager" class="org.jboss.demos.ioc.install.RepositoryManager"> <install method="addEntry"> <parameter><inject fromContext="name"/></parameter> <parameter><this/></parameter> </install> <uninstall method="removeEntry"> <parameter><inject fromContext="name"/></parameter> </uninstall> </bean> <bean name="Entry" class="org.jboss.demos.ioc.install.SimpleEntry"> <install bean="RepositoryManager" method="addEntry" state="Instantiated"> <parameter><inject fromContext="name"/></parameter> <parameter><this/></parameter> </install> <uninstall bean="RepositoryManager" method="removeEntry" state="Configured"> <parameter><inject fromContext="name"/></parameter> </uninstall> </bean> 7.14. Lazy Mock Vous avez peut-être une dépendance de bean que vous n'utilisez que très rarement, mais qui prend longtemps à configurer. Vous pouvez utiliser un Lazy Mock du bean, comme expliqué dans Exemple 7.24, « Lazy Mock », pour résoudre la dépendance. Quand vous avez besoin d'un bean, invoquez et utilisez le bean cible, en espérant qu'il a maintenant été installé. 73 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 7.24 . Lazy Mock <bean name="lazyA" class="org.jboss.demos.ioc.lazy.LazyImpl"> <constructor> <parameter> <lazy bean="lazyB"> <interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface> </lazy> </parameter> </constructor> </bean> <bean name="lazyB" class="org.jboss.demos.ioc.lazy.LazyImpl"> <constructor> <parameter> <lazy bean="lazyA"> <interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface> </lazy> </parameter> </constructor> </bean> <lazy name="anotherLazy" bean="Pojo" exposeClass="true"/> <bean name="Pojo" class="org.jboss.demos.ioc.lazy.Pojo"/> 7.15. Cycle de vie Le micro-conteneur utilise par défaut les méthodes create, start, et destroy quand il évolue entre les différents états. Vous ne souhaitez sans doute pas que le Microcontainer les invoque. Pour cette raison, un drapeau ignore est disponible. Exemple 7.25. Cycles de vie de beans <bean name="FullLifecycleBean-3" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"/> <bean name="FullLifecycleBean-2" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"> <create ignored="true"/> </bean> <bean name="FullLifecycleBean-1" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"> <start ignored="true"/> </bean> 74 Chapitre 8. The Virtual File System Chapitre 8. The Virtual File System Duplication of resource-handling code is a common problem for developers. In most cases, the code deals with determining information about a particular resource, which might be a file, a directory, or, in the case of a JAR, a remote URL. Another duplication problem is code for the processing of nested archives. Exemple 8.1, « Resource Duplication Problem » illustrates the problem. 75 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 8.1. Resource Duplication Problem 76 Chapitre 8. The Virtual File System public static URL[] search(ClassLoader cl, String prefix, String suffix) throws IOException { Enumeration[] e = new Enumeration[]{ cl.getResources(prefix), cl.getResources(prefix + "MANIFEST.MF") }; Set all = new LinkedHashSet(); URL url; URLConnection conn; JarFile jarFile; for (int i = 0, s = e.length; i < s; ++i) { while (e[i].hasMoreElements()) { url = (URL)e[i].nextElement(); conn = url.openConnection(); conn.setUseCaches(false); conn.setDefaultUseCaches(false); if (conn instanceof JarURLConnection) { jarFile = ((JarURLConnection)conn).getJarFile(); } else { jarFile = getAlternativeJarFile(url); } if (jarFile != null) { searchJar(cl, all, jarFile, prefix, suffix); } else { boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), "UTF-8")), suffix); if (searchDone == false) { searchFromURL(all, prefix, suffix, url); } } } } return (URL[])all.toArray(new URL[all.size()]); } private static boolean searchDir(Set result, File file, String suffix) throws IOException { if (file.exists() && file.isDirectory()) { File[] fc = file.listFiles(); String path; for (int i = 0; i < fc.length; i++) { path = fc[i].getAbsolutePath(); if (fc[i].isDirectory()) { searchDir(result, fc[i], suffix); } else if (path.endsWith(suffix)) { 77 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer result.add(fc[i].toURL()); } } return true; } return false; } T here are also many problems with file locking on Windows systems, forcing developers to copy all hotdeployable archives to another location to prevent locking those in deploy folders (which would prevent their deletion and file-system based undeploy). File locking is a major problem whose only solution used to be centralizing all the resource loading code in one place. T he VFS project was created solve all of these issues. VFS stands for Virtual File System. 8.1. VFS Public API VFS is used for two main purposes, as shown in Uses for VFS. Uses for VFS simple resource navigation visitor pattern API (Application Programmer Interface) As mentioned, in plain JDK, handling and navigating resources are complex. You must always check the resource type, and these checks can be cumbersome. VFS abstracts resources into a single resource type, VirtualFile. 78 Chapitre 8. The Virtual File System Exemple 8.2. T he VirtualFile Resource T ype 79 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer public class VirtualFile implements Serializable { /** * Get certificates. * * @return the certificates associated with this virtual file */ Certificate[] getCertificates() /** * Get the simple VF name (X.java) * * @return the simple file name * @throws IllegalStateException if the file is closed */ String getName() /** * Get the VFS relative path name (org/jboss/X.java) * * @return the VFS relative path name * @throws IllegalStateException if the file is closed */ String getPathName() /** * Get the VF URL (file://root/org/jboss/X.java) * * @return the full URL to the VF in the VFS. * @throws MalformedURLException if a url cannot be parsed * @throws URISyntaxException if a uri cannot be parsed * @throws IllegalStateException if the file is closed */ URL toURL() throws MalformedURLException, URISyntaxException /** * Get the VF URI (file://root/org/jboss/X.java) * * @return the full URI to the VF in the VFS. * @throws URISyntaxException if a uri cannot be parsed * @throws IllegalStateException if the file is closed * @throws MalformedURLException for a bad url */ URI toURI() throws MalformedURLException, URISyntaxException /** * When the file was last modified * * @return the last modified time * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ long getLastModified() throws IOException /** * Returns true if the file has been modified since this method was last called * Last modified time is initialized at handler instantiation. * * @return true if modifed, false otherwise * @throws IOException for any error 80 Chapitre 8. The Virtual File System */ boolean hasBeenModified() throws IOException /** * Get the size * * @return the size * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ long getSize() throws IOException /** * Tests whether the underlying implementation file still exists. * @return true if the file exists, false otherwise. * @throws IOException - thrown on failure to detect existence. */ boolean exists() throws IOException /** * Whether it is a simple leaf of the VFS, * i.e. whether it can contain other files * * @return true if a simple file. * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ boolean isLeaf() throws IOException /** * Is the file archive. * * @return true if archive, false otherwise * @throws IOException for any error */ boolean isArchive() throws IOException /** * Whether it is hidden * * @return true when hidden * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ boolean isHidden() throws IOException /** * Access the file contents. * * @return an InputStream for the file contents. * @throws IOException for any error accessing the file system * @throws IllegalStateException if the file is closed */ InputStream openStream() throws IOException /** * Do file cleanup. * * e.g. delete temp files */ 81 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer void cleanup() /** * Close the file resources (stream, etc.) */ void close() /** * Delete this virtual file * * @return true if file was deleted * @throws IOException if an error occurs */ boolean delete() throws IOException /** * Delete this virtual file * * @param gracePeriod max time to wait for any locks (in milliseconds) * @return true if file was deleted * @throws IOException if an error occurs */ boolean delete(int gracePeriod) throws IOException /** * Get the VFS instance for this virtual file * * @return the VFS * @throws IllegalStateException if the file is closed */ VFS getVFS() /** * Get the parent * * @return the parent or null if there is no parent * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ VirtualFile getParent() throws IOException /** * Get a child * * @param path the path * @return the child or <code>null</code> if not found * @throws IOException for any problem accessing the VFS * @throws IllegalArgumentException if the path is null * @throws IllegalStateException if the file is closed or it is a leaf node */ VirtualFile getChild(String path) throws IOException /** * Get the children * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ List<VirtualFile> getChildren() throws IOException 82 Chapitre 8. The Virtual File System /** * Get the children * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ List<VirtualFile> getChildren(VirtualFileFilter filter) throws IOException /** * Get all the children recursively<p> * * This always uses {@link VisitorAttributes#RECURSE} * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ List<VirtualFile> getChildrenRecursively() throws IOException /** * Get all the children recursively<p> * * This always uses {@link VisitorAttributes#RECURSE} * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ List<VirtualFile> getChildrenRecursively(VirtualFileFilter filter) throws IOException /** * Visit the virtual file system * * @param visitor the visitor * @throws IOException for any problem accessing the virtual file system * @throws IllegalArgumentException if the visitor is null * @throws IllegalStateException if the file is closed */ void visit(VirtualFileVisitor visitor) throws IOException } All of the usual read-only File System operations are available, plus a few options to cleanup or delete the resource. Cleanup or deletion handling is needed when dealing with some internal temporary files, such as files created to handle nested jars. T o switch from the JDK's File or URL resource handling to new VirtualFile you need a root VirtualFile, which is provided by the VFS class, with the help of URL or URI parameter. 83 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 8.3. Using the VFS Class 84 Chapitre 8. The Virtual File System public class VFS { /** * Get the virtual file system for a root uri * * @param rootURI the root URI * @return the virtual file system * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VFS getVFS(URI rootURI) throws IOException /** * Create new root * * @param rootURI the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile createNewRoot(URI rootURI) throws IOException /** * Get the root virtual file * * @param rootURI the root uri * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VirtualFile getRoot(URI rootURI) throws IOException /** * Get the virtual file system for a root url * * @param rootURL the root url * @return the virtual file system * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VFS getVFS(URL rootURL) throws IOException /** * Create new root * * @param rootURL the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile createNewRoot(URL rootURL) throws IOException /** * Get the root virtual file * * @param rootURL the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile getRoot(URL rootURL) throws IOException 85 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer /** * Get the root file of this VFS * * @return the root * @throws IOException for any problem accessing the VFS */ VirtualFile getRoot() throws IOException } T he three different methods look similar. getVFS createNewRoot getRoot getVFS returns a VFS instance but does not yet create a VirtualFile instance. T his is important because there are methods which help with configuring a VFS instance (see VFS class API javadocs), before instructing it to create a VirtualFile root. T he other two methods, on the other hand, use default settings for root creation. T he difference between createNewRoot and getRoot is in caching details, which are covered later. Exemple 8.4 . Using getVFS URL rootURL = ...; // get root url VFS vfs = VFS.getVFS(rootURL); // configure vfs instance VirtualFile root1 = vfs.getRoot(); // or you can get root directly VirtualFile root2 = VFS.crateNewRoot(rootURL); VirtualFile root3 = VFS.getRoot(rootURL); Another useful thing about VFS API is its implementation of a proper visitor pattern. It is very simple to recursively gather different resources, a task which is difficult to do with plain JDK resource loading. 86 Chapitre 8. The Virtual File System Exemple 8.5. Recursively Gathering Resources public interface VirtualFileVisitor { /** * Get the search attribues for this visitor * * @return the attributes */ VisitorAttributes getAttributes(); /** * Visit a virtual file * * @param virtualFile the virtual file being visited */ void visit(VirtualFile virtualFile); } VirtualFile root = ...; // get root VirtualFileVisitor visitor = new SuffixVisitor(".class"); // get all classes root.visit(visitor); 8.2. VFS Architecture While the public API is quite intuitive, real implementation details add complexity. Some concepts need to be explained in more detail. Each time you create a VFS instance, its matching VFSContext instance is created. T his creation is done via VFSContextFactory. Different protocols map to different VFSContextFactory instances. For example, file/vfsfile maps to FileSystem ContextFactory, while zip/vfszip maps to ZipEntryContextFactory. Each time a VirtualFile instance is created, its matching VirtualFileHandler is created. T his VirtualFileHandler instance knows how to handle different resource types properly; the VirtualFile API only delegates invocations to its VirtualFileHandler reference. T he VFSContext instance knows how to create VirtualFileHandler instances accordingly to a resource type. For example, Z ipEntryContextFactory creates ZipEntryContext, which then creates ZipEntryHandler. 8.3. Existing Implementations Apart from files, directories (FileHandler) and zip archives (Z ipEntryHandler) the Microcontainer also supports other more advanced use cases. T he first one is Assembled, which is similar to what Eclipse calls Linked Resources. Its purpose is to take existing resources from different trees, and "mock" them into single resource tree. 87 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 8.6. Implementation of Assembled VirtualFileHandlers AssembledDirectory sar = AssembledContextFactory.getInstance().create("assembled.sar"); URL url = getResource("/vfs/test/jar1.jar"); VirtualFile jar1 = VFS.getRoot(url); sar.addChild(jar1); url = getResource("/tmp/app/ext.jar"); VirtualFile ext1 = VFS.getRoot(url); sar.addChild(ext); AssembledDirectory metainf = sar.mkdir("META-INF"); url = getResource("/config/jboss-service.xml"); VirtualFile serviceVF = VFS.getRoot(url); metainf.addChild(serviceVF); AssembledDirectory app = sar.mkdir("app.jar"); url = getResource("/app/someapp/classes"); VirtualFile appVF = VFS.getRoot(url); app.addPath(appVF, new SuffixFilter(".class")); Another implementation is in-memory files. T his implementation arose out of a need to easily handle AOP generated bytes. Instead of using temporary files, you can drop bytes into in-memory VirtualFileHandlers. Exemple 8.7. Implementation of In-Memory VirtualFileHandlers URL url = new URL("vfsmemory://aopdomain/org/acme/test/Test.class"); byte[] bytes = ...; // some AOP generated class bytes MemoryFileFactory.putFile(url, bytes); VirtualFile classFile = VFS.getVirtualFile(new URL("vfsmemory://aopdomain"), "org/acme/test/Test.class"); InputStream bis = classFile.openStream(); // e.g. load class from input stream 8.4. Extension Hooks It is easy to extend VFS with a new protocol, similar to what we've done with Assem bled and Mem ory. All you need is a combination of VFSContexFactory, VFSContext, VirtualFileHandler, FileHandlerPlugin, and URLStream Handler implementations. T he VFSContextFactory is trivial, while the others depend on the complexity of your task. You could implement rar, tar, gzip, or even rem ote access. After implementing a new protocol, register the new VFSContextFactory with VFSContextFactoryLocator. 8.5. Features One of the first major problems the Microcontainer developers faced was proper usage of nested resources, more specifically nested jar files: For example, normal ear deployments: 88 Chapitre 8. The Virtual File System gem a.ear/ui.war/WEB-INF/lib/struts.jar. In order to read contents of struts.jar we have two options: handle resources in memory create top level temporary copies of nested jars, recursively T he first option is easier to implement, but it's very memory-consuming, requiring potentially large applications to reside in memory. T he other approach leaves behind a large number of temporary files, which should be invisible to the end user and therefore should disappear after undeployment. Consider the following scenario: A user accesses a VFS URL instance, which points to some nested resource. T he way plain VFS would handle this is to re-create the entire path from scratch: it would unpack nested resources over and over again. T his leads to a great number of temporary files. T he Microcontainer avoids this by using VFSRegistry, VFSCache, and T em pInfo. When you ask for VirtualFile over VFS (getRoot, not createNewRoot), VFS asks the VFSRegistry implementation to provide the file. T he existing DefaultVFSRegistry first checks if a matching root VFSContext exists for the provided URI. If it does, DefaultVFSRegistry first tries to navigate to the existing T em pInfo (link to a temporary files), falling back to regular navigation if no such temporary file exists. In this way you completely reuse any temporary files which have already been unpacked, saving time and disk space. If no matching VFSContext is found in cache, the code will create a new VFSCache entry and continue with default navigation. Determining how the VFSCache handles cached VFSContext entries depends on the implementation used. VFSCache is configurable via VFSCacheFactory. By default, nothing is cached, but there are a few useful existing VFSCache implementations, using algorithms such as Least Recently Used (LRU) or tim ed cache. 89 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Chapitre 9. La couche de chargement de classe JBoss a toujours eu une façon unique de traiter les chargement de classes, et la nouvelle couche de chargement de classes du Microcontainer n'est pas différente sur ce point. Le chargement de classes est un complément additionnel que vous pouvez utiliser quand vous souhaitez procéder à un chargement de classe non conventionnel par défaut. Avec l'augmentation des chargements de style OSGi, et un certain nombre de nouvelles spécifications de chargement de classes de Java à l'horizon, les changements apportés à la couche de chargement de classes de EAP 5.1 sont utiles et tombent à point. La couche de chargement de classes du Microcontainer est une couche abstraite. La plupart des informations sont cachées derrière des méthodes privées ou des méthodes de package-privés, sans compromis sur l'extensibilité ou la fonctionnalité possible dans les classes publiques ou avec les méthodes qui font l'API. Cela signifie que vous codez en fonction de la politique et non pas en fonction des informations du chargeur de classes. Le projet de chargement de classes et divisé en 3 sous-projets chargeur de classes chargement de classes chargement de classes-vfs classloader contient une extension personnalisée java.lang.ClassLoader sans aucune politique de chargement de classes particulière. Une politique de chargement de classes doit comprendre des instructions sur l'endroit à partir duquel décharger et comment charger. Classloading est une extension des mécanismes de dépendance du Microcontainer. Son implémentation endorsée-VFS est classloading-vfs. Voir Chapitre 8, The Virtual File System pour obtenir plus d'informations sur VFS. 9.1. Chargeur de classes L'implémentation ClassLoader supporte les politiques complémentaires et consititue en elle-même une classe finale, qui n'est pas sensée être modifiée. Pour écrire vos propres implémentations de chargement de classe, écrire une politique ClassLoaderPolicy qui fournit un simple API pour trouver les ressources et les classes, et pour spécifier d'autres règles associées au chargeur de classes. Pour personnaliser le chargement de classes, instancier un ClassLoaderPolicy et enregistrez-le avec ClassLoaderSystem pour créer un ClassLoader personnalisé. Vous pouvez également créer un ClassLoaderDom ain pour partitionner le ClassLoaderSystem . La couche ClassLoader inclut également l'implémentation du modèle DelegateLoader, du chargement de classes, des filtres de ressource, et des politiques de délégation parent-enfant, etc... L'exécution de JMX a permis d'exposer la stratégie utilisée pour chaque chargeur de classes. Il fournit également des statistiques de chargement de classes et de des méthodes de débogage pour aider à déterminer à partir d'où viennent les chargements. 90 Chapitre 9. La couche de chargement de classe Exemple 9.1. Classe ClassLoaderPolicy Le ClassLoaderPolicy contrôle la façon dont le chargement de classes opère. public abstract class ClassLoaderPolicy extends BaseClassLoaderPolicy { public DelegateLoader getExported() public String[] getPackageNames() protected List<? extends DelegateLoader> getDelegates() protected boolean isImportAll() protected boolean isCacheable() protected boolean isBlackListable() public abstract URL getResource(String path); public InputStream getResourceAsStream(String path) public abstract void getResources(String name, Set<URL> urls) throws IOException; protected ProtectionDomain getProtectionDomain(String className, String path) public PackageInformation getPackageInformation(String packageName) public PackageInformation getClassPackageInformation(String className, String packageName) protected ClassLoader isJDKRequest(String name) } } Voici deux exemples de ClassLoaderPolicy. Le premier extrait les ressources fondées sur des expressions régulières, tandis que le second s'occupe des ressources chiffrées. 91 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 9.2. ClassLoaderPolicy avec Support d'expressions régulières 92 Chapitre 9. La couche de chargement de classe public class RegexpClassLoaderPolicy extends ClassLoaderPolicy { private VirtualFile[] roots; private String[] packageNames; public RegexpClassLoaderPolicy(VirtualFile[] roots) { this.roots = roots; } @Override public String[] getPackageNames() { if (packageNames == null) { Set<String> exportedPackages = PackageVisitor.determineAllPackages(roots, null, ExportAll.NON_EMPTY, null, null, null); packageNames = exportedPackages.toArray(new String[exportedPackages.size()]); } return packageNames; } protected Pattern createPattern(String regexp) { boolean outside = true; StringBuilder builder = new StringBuilder(); for (int i = 0; i < regexp.length(); i++) { char ch = regexp.charAt(i); if ((ch == '[' || ch == ']' || ch == '.') && escaped(regexp, i) == false) { switch (ch) { case '[' : outside = false; break; case ']' : outside = true; break; case '.' : if (outside) builder.append("\\"); break; } } builder.append(ch); } return Pattern.compile(builder.toString()); } protected boolean escaped(String regexp, int i) { return i > 0 && regexp.charAt(i - 1) == '\\'; } public URL getResource(String path) { Pattern pattern = createPattern(path); for (VirtualFile root : roots) { URL url = findURL(root, root, pattern); if (url != null) return url; } return null; } 93 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer private URL findURL(VirtualFile root, VirtualFile file, Pattern pattern) { try { String path = AbstractStructureDeployer.getRelativePath(root, file); Matcher matcher = pattern.matcher(path); if (matcher.matches()) return file.toURL(); List<VirtualFile> children = file.getChildren(); for (VirtualFile child : children) { URL url = findURL(root, child, pattern); if (url != null) return url; } return null; } catch (Exception e) { throw new RuntimeException(e); } } public void getResources(String name, Set<URL> urls) throws IOException { Pattern pattern = createPattern(name); for (VirtualFile root : roots) { RegexpVisitor visitor = new RegexpVisitor(root, pattern); root.visit(visitor); urls.addAll(visitor.getUrls()); } } private static class RegexpVisitor implements VirtualFileVisitor { private VirtualFile root; private Pattern pattern; private Set<URL> urls = new HashSet<URL>(); private RegexpVisitor(VirtualFile root, Pattern pattern) { this.root = root; this.pattern = pattern; } public VisitorAttributes getAttributes() { return VisitorAttributes.RECURSE_LEAVES_ONLY; } public void visit(VirtualFile file) { try { String path = AbstractStructureDeployer.getRelativePath(root, file); Matcher matcher = pattern.matcher(path); if (matcher.matches()) 94 Chapitre 9. La couche de chargement de classe urls.add(file.toURL()); } catch (Exception e) { throw new RuntimeException(e); } } public Set<URL> getUrls() { return urls; } } } RegexpClassLoaderPolicy utilise un mécanisme simpliste pour trouver les ressources qui correspondent. Les implémentations réelles sont normalement plus complètes et plus élégantes. public class RegexpService extends PrintService { public void start() throws Exception { System.out.println(); ClassLoader cl = getClass().getClassLoader(); Enumeration<URL> urls = cl.getResources("config/[^.]+\\.[^.]{1,4}"); while (urls.hasMoreElements()) { URL url = urls.nextElement(); print(url.openStream(), url.toExternalForm()); } } } Le service regexp utilise des modèles d'expression config/[^.]+\\.[^.]{1,4 } pour lister les ressources sous le répertoire/ config/. La longueur du suffixe est limitée, de façon à ce que les noms de fichiers comme excluded.properties soient ignorés. 95 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 9.3. ClassLoaderPolicy avec support de chiffrement public class CrypterClassLoaderPolicy extends VFSClassLoaderPolicy { private Crypter crypter; public CrypterClassLoaderPolicy(String name, VirtualFile[] roots, VirtualFile[] excludedRoots, Crypter crypter) { super(name, roots, excludedRoots); this.crypter = crypter; } @Override public URL getResource(String path) { try { URL resource = super.getResource(path); return wrap(resource); } catch (IOException e) { throw new RuntimeException(e); } } @Override public InputStream getResourceAsStream(String path) { InputStream stream = super.getResourceAsStream(path); return crypter.crypt(stream); } @Override public void getResources(String name, Set<URL> urls) throws IOException { super.getResources(name, urls); Set<URL> temp = new HashSet<URL>(urls.size()); for (URL url : urls) { temp.add(wrap(url)); } urls.clear(); urls.addAll(temp); } protected URL wrap(URL url) throws IOException { return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile(), new CrypterURLStreamHandler(crypter)); } } Exemple 9.3, « ClassLoaderPolicy avec support de chiffrement » montre comment encrypter les JAR. Vous pouvez configurer quelles ressources encrypter pour spécifier un filtre. Ici, tout est chiffré, mis à part les contenus du répertoire MET A-INF/. 96 Chapitre 9. La couche de chargement de classe public class EncryptedService extends PrintService { public void start() throws Exception { ClassLoader cl = getClass().getClassLoader(); URL url = cl.getResource("config/settings.txt"); if (url == null) throw new IllegalArgumentException("No such settings.txt."); InputStream is = url.openStream(); print(is, "Printing settings:\n"); is = cl.getResourceAsStream("config/properties.xml"); if (is == null) throw new IllegalArgumentException("No such properties.xml."); print(is, "\nPrinting properties:\n"); } } Ce service affiche les contenus de deux fichiers de configuration. Il montre que le décryptage de toute ressource chiffrée est cachée derrière la couche de chargement de classes. Pour pouvoir tester cela, vous pourrez soit chiffrer le module de la politique vous-même ou en utiliser une qui est chiffrée. Pour actionner tout cela, vous aurez besoin d'associer correctement EncryptedService à ClassLoaderSystem et aux déployeurs. On discute le partitionnement du ClassLoaderSystem dans un chapitre suivant.\n 9.2. Chargement de classes Au lieu d'utiliser l'abstraction ClassLoader directement, vous pouvez créer les modules ClassLoading qui contiennent des déclarations des dépendances du ClassLoader. Une fois que les dépendances ont été spécifiées, les politiques ClassLoaderPolicy sont construites et cablées en fonction. Pour faciliter la définition des ClassLoaders avant qu'elles n'existent, l'abstraction inclut un modèle de ClassLoadingMetaData. Le ClassLoadingMetaData peut être présenté comme un Managed Object du nouveau service de profil JBoss EAP. Cela aide les administrateurs de système à gérer des informations de politique plus abstraits que les informations d'implémentation. 97 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 9.4 . ClassLoadingMetaData présentées comme Managed Object public class ClassLoadingMetaData extends NameAndVersionSupport { /** The serialVersionUID */ private static final long serialVersionUID = -2782951093046585620L; /** The classloading domain */ private String domain; /** The parent domain */ private String parentDomain; /** Whether to make a subdeployment classloader a top-level classloader */ private boolean topLevelClassLoader = false; /** Whether to enforce j2se classloading compliance */ private boolean j2seClassLoadingCompliance = true; /** Whether we are cacheable */ private boolean cacheable = true; /** Whether we are blacklistable */ private boolean blackListable = true; /** Whether to export all */ private ExportAll exportAll; /** Whether to import all */ private boolean importAll; /** The included packages */ private String includedPackages; /** The excluded packages */ private String excludedPackages; /** The excluded for export */ private String excludedExportPackages; /** The included packages */ private ClassFilter included; /** The excluded packages */ private ClassFilter excluded; /** The excluded for export */ private ClassFilter excludedExport; /** The requirements */ private RequirementsMetaData requirements = new RequirementsMetaData(); /** The capabilities */ private CapabilitiesMetaData capabilities = new CapabilitiesMetaData(); ... setters & getters 98 Chapitre 9. La couche de chargement de classe Exemple 9.5, « API ClassLoading défini dans XML » et Exemple 9.6, « API ClassLoading défini dans Java » nous montre l'API ClassLoading (chargement de classes) défini dans XML et Java respectivement. Exemple 9.5. API ClassLoading défini dans XML <classloading xmlns="urn:jboss:classloading:1.0" name="ptd-jsf-1.0.war" domain="ptd-jsf-1.0.war" parent-domain="ptd-ear-1.0.ear" export-all="NON_EMPTY" import-all="true" parent-first="true"/> Exemple 9.6. API ClassLoading défini dans Java ClassLoadingMetaData clmd = new ClassLoadingMetaData(); if (name != null) clmd.setDomain(name + "_Domain"); clmd.setParentDomain(parentDomain); clmd.setImportAll(true); clmd.setExportAll(ExportAll.NON_EMPTY); clmd.setVersion(Version.DEFAULT_VERSION); Vous pouvez ajouter ClaasLoadingMetaData à votre déploiement soit par programmation, ou déclarativement, via jboss-classloading.xm l. Exemple 9.7. Ajouter ClassLoadingMetaData en utilisant jboss-classloading.xm l <classloading xmlns="urn:jboss:classloading:1.0" domain="DefaultDomain" top-level-classloader="true" export-all="NON_EMPTY" import-all="true"> </classloading> Le DefautDomain (domaine par défaut) est partagé par toutes les applications qui ne définissent pas leur propre domaine. 99 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 9.8. Isolations typiques niveau-domaine <classloading xmlns="urn:jboss:classloading:1.0" domain="IsolatedDomain" export-all="NON_EMPTY" import-all="true"> </classloading> Exemple 9.9. Isolation avec un parent spécifique <classloading xmlns="urn:jboss:classloading:1.0" domain="IsolatedWithParentDomain" parent-domain="DefaultDomain" export-all="NON_EMPTY" import-all="true"> </classloading> Exemple 9.10. Incompatibilité avec j2seClassLoadingCompliance <classloading xmlns="urn:jboss:classloading:1.0" parent-first="false"> </classloading> Les déploiements .war utilisent cette méthode par défaut. Au lieu de faire des recherches parentd'abord, vous pouvez chercher tout d'abord vos propres ressources. Exemple 9.11. Implémentation OSGi typique <classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <package name="org.jboss.dependency.spi"/> </requirements> <capabilities> <package name="org.jboss.cache.api"/> <package name="org.jboss.kernel.spi"/> </capabilities> </classloading> 100 Chapitre 9. La couche de chargement de classe Exemple 9.12. Importer et exporter des modules et des bibliothèques complètes, au lieu de packages à fine granularité. <classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <module name="jboss-reflect.jar"/> </requirements> <capabilities> <module name="jboss-cache.jar"/> </capabilities> </classloading> <classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <package name="si.acme.foobar"/> <module name="jboss-reflect.jar"/> </requirements> <capabilities> <package name="org.alesj.cl"/> <module name="jboss-cache.jar"/> </capabilities> </classloading> Vous pouvez également mélanger les types d'exigences et les types de capacités, à l'aide de packages et de modules. Le sous-projet de chargement de classes utilise une toute petite implémentation de modèle-visiteurressource. Dans le projet ClassLoader, la connexion entre le déploiement et le chargement de classes est fait par l'intermédiaire de la classe Module, qui contient toutes les informations demandées en vue d'appliquer correctement les restrictions sur le modèle visiteur, comme le filtrage. 101 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 9.13. Les interfaces ResourceVisitor et ResourceContext public interface ResourceVisitor { ResourceFilter getFilter(); void visit(ResourceContext resource); } public interface ResourceContext { URL getUrl(); ClassLoader getClassLoader(); String getResourceName(); String getClassName(); boolean isClass(); Class<?> loadClass(); InputStream getInputStream() throws IOException; byte[] getBytes() throws IOException; } Pour utiliser le module, instancier votre instance ResourceVisitor et passez-le à la méthode Module::visit. Cette fonctionnalité est utilisée dans le framework de déploiement pour indexer l'utilisation des annotations dans les déploiements. 9.3. ClassLoading VFS Ces exemples fournissent une implémentation de la politique ClassLoaderPolicy qui utilise un projet JBoss Virtual File System pour charger des classes et des ressources. Vous pouvez utiliser cette idée directement ou en combinaison à un framework de chargement de classes. En option, vous pouvez installer des modules dans la configuration du Microcontainer. 102 Chapitre 9. La couche de chargement de classe Exemple 9.14 . Déployeur du module de chargement de classes. <deployment xmlns="urn:jboss:bean-deployer:2.0"> <classloader name="anys-classloader" xmlns="urn:jboss:classloader:1.0" importall="true" domain="Anys" parent-domain="DefaultDomain"> <capabilities> <package name="org.jboss.test.deployers.vfs.reflect.support.web"/> </capabilities> <root>${jboss.tests.url}</root> </classloader> <bean name="AnyServlet" class="org.jboss.test.deployers.vfs.reflect.support.web.AnyServlet"> <classloader><inject bean="anys-classloader:0.0.0"/></classloader> </bean> </deployment> La classe VFSClassLoaderFactory transforme le déployeur XML en VFSClassLoaderPolicyModule, qui crée l'instance ClassLoader. Vous pourrez alors utiliser cette nouvelle instance de ClassLoader avec vos beans. Note VFSClassLoaderFactoryétend ClassLoadingMetaData, donc tous les exemples liés à ClassLoadingMetaData s'appliquent dans ce cas également. 103 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Chapitre 10. The Virtual Deployment Framework T he new Virtual Deployment Framework (VDF) is an improved way to manage deployers in the Microcontainer. T his chapter details some of its useful features. 10.1. Agnostic Handling of Deployment Types T he traditional type of virtual deployment is based on classes which already exist in a shared classspace or domain. In this case, the end product is a new service installed onto the server from your main client. T he traditional way to do this is to upload a descriptor file. T he new VDF simplifies this process by passing over the bytes and serializing them into a Deploym ent instance. T he other type of deployment, which extends the first one, is a plain file-system-based deployment, backed up by Microcontainer VFS. T his approach is described in more detail in Chapitre 8, The Virtual File System. 10.2. Separation of Structure Recognition From Deployment lifecycle logic In order to do any real work on top of a deployment, you must first understand its structure, including its classpaths and metadata locations. Metadata locations include the configuration files such as m y-jboss-beans.xm l, web.xm l, ejbjar.xm l. Classpaths are classloader roots, such as WEB-INF/classes or m yapp.ear/lib. Bearing the structure in mind, you can proceed to actual deployment handling. A T ypical Deployment Lifecycle 1. T he MainDeployer passes the deployment to the set of StructuralDeployers for recognition, and receives back a Deployment context. 2. Next, the MainDeployer passes the resulting Deployment context to the Deployers for handling by the appropriate Deployer. In this way, the MainDeployer is a broker with the responsibility of deciding which Deployers to use. In the case of virtual or programmatic deployment, an existing predetermined StructureMetaData information reads the structure information and handles it in one of the ways explained in Handling StructuredMetaData Information. Handling StructuredMetaData Information VFS-based deployments the structure recognition is forwarded to a set of StructureDeployers. JEE-specification-defined structures we have matching StructureDeployer implementations: EarStructure WarStructure JarStructure 104 Chapitre 10. The Virtual D eployment Framework DeclarativeStructures looks for MET A-INF/jboss-structure.xm l file inside your deployment, and parses it to construct a proper StructureMetaData. FileStructures only recognizes known configuration files, such as files like -jboss-beans.xm l or service.xm l. Exemple 10.1. An example of jboss-structure.xm l <structure> <context comparator="org.jboss.test.deployment.test.SomeDeploymentComparatorTop"> <path name=""/> <metaDataPath> <path name="META-INF"/> </metaDataPath> <classpath> <path name="lib" suffixes=".jar"/> </classpath> </context> </structure> In the case of EarStructure, first recognize a top level deployment, then recursively process subdeployments. You can implement a custom StructureDeployer with the help of the generic GroupingStructure class provided by the generic StructureDeployer interface. After you have a recognized deployment structure, you can pass it to real deployers. T he Deployers object knows how to deal with the real deployers, using a chain of deployers per Deploym entStage. 105 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Exemple 10.2. Deployment Stages public interface DeploymentStages { /** The not installed stage - nothing is done here */ DeploymentStage NOT_INSTALLED = new DeploymentStage("Not Installed"); /** The pre parse stage - where pre parsing stuff can be prepared; altDD, ignore, ... */ DeploymentStage PRE_PARSE = new DeploymentStage("PreParse", NOT_INSTALLED); /** The parse stage - where metadata is read */ DeploymentStage PARSE = new DeploymentStage("Parse", PRE_PARSE); /** The post parse stage - where metadata can be fixed up */ DeploymentStage POST_PARSE = new DeploymentStage("PostParse", PARSE); /** The pre describe stage - where default dependencies metadata can be created */ DeploymentStage PRE_DESCRIBE = new DeploymentStage("PreDescribe", POST_PARSE); /** The describe stage - where dependencies are established */ DeploymentStage DESCRIBE = new DeploymentStage("Describe", PRE_DESCRIBE); /** The classloader stage - where classloaders are created */ DeploymentStage CLASSLOADER = new DeploymentStage("ClassLoader", DESCRIBE); /** The post classloader stage - e.g. aop */ DeploymentStage POST_CLASSLOADER = new DeploymentStage("PostClassLoader", CLASSLOADER); /** The pre real stage - where before real deployments are done */ DeploymentStage PRE_REAL = new DeploymentStage("PreReal", POST_CLASSLOADER); /** The real stage - where real deployment processing is done */ DeploymentStage REAL = new DeploymentStage("Real", PRE_REAL); /** The installed stage - could be used to provide valve in future? */ DeploymentStage INSTALLED = new DeploymentStage("Installed", REAL); } Preexisting deployment stages are mapped to the Microcontainer's built-in controller states. T hey provide a deployment-lifecycle-centric view of generic controller states. Inside Deployers, the deployment is converted into the Microcontainer's component Deploym entControllerContext. T he Microcontainer's state machine handles dependencies. Deployments are handled sequentially by deployment stage. For each deployer, the entire deployed hierarchy order is handled, using the deployer's parent-first property. T his property is set to true by default. You can also specify which hierarchy levels your deployer handles. You can choose all, top level, com ponents only, or no com ponents. T he way the Microcontainer handles component models and dependency handling applies here as well. If there are unresolved dependencies, the deployment will wait in the current state, potentially reporting an error if the current state is not the required state. 106 Chapitre 10. The Virtual D eployment Framework an error if the current state is not the required state. Adding a new deployer is accomplished by extending one of the many existing helper deployers. Some deployers actually need VFS backed deployment, while others can use a general deployment. In most cases the parsing deployers are the ones that need VFS backing. Avertissement Also be aware that deployers run recursively through every deployment, sub-deployment, and component. Your code needs to determine, as early in the process as possible, whether the deployer should handle the current deployment or not. Exemple 10.3. Simple Deployer which Outputs Information About Its Deployment public class StdioDeployer extends AbstractDeployer { public void deploy(DeploymentUnit unit) throws DeploymentException { System.out.println("Deploying unit: " + unit); } @Override public void undeploy(DeploymentUnit unit) { System.out.println("Undeploying unit: " + unit); } } Add this description into one of the -jboss-beans.xm l files in deployers/ directory in JBoss Application Server, and MainDeployerIm pl bean will pick up this deployer via the Microcontainer's IoC callback handling. Exemple 10.4 . Simple Deployment Descriptor <bean name="StdioDeployer" class="org.jboss.acme.StdioDeployer"/> 10.3. Natural Flow Control in the form of Attachments VDF includes a mechanism called attachments, which facilitates the passing of information from one deployer to the next. Attachments are implemented as a slightly-enhanced java.util.Map, whose entries each represent an attachment. Some deployers are producers, while others are consumers. T he same deployer can also perform both functions. Some deployers create metadata or utility instances, putting them into the attachments map. Other deployers only declare their need for these attachments and pull the data from the attachments map, before doing additional work on that data. Natural order refers to the way that deployers are ordered. A common natural order uses the relative terms before and after. However, with the attachments mechanism already in place, you can order deployers by the way in which they produce and/or consume the attachments. 107 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Each attachment has a key, and deployers pass keys to the attachments they produce. If the deployer produces an attachment, then that key is called output. If the deployer consumes an attachment, then that key is called input. Deployers have ordinary inputs and required inputs. Ordinary inputs are only used to help determine the natural order. Required inputs also help determine order, but they have another function too. T hey help to determine if the deployer is actually relevant for the given deployment, by checking to see if an attachment corresponding to that required input exists in the attachments map. Avertissement While relative ordering is still supported, it is considered bad practice, and could be desupported in future releases. 10.4. Client, User, and Server Usage and Implementation Details T hese features hide the implementation details, making the usage less error-prone, while at the same time streamlining the development process. T he goal is for clients to only see a Deployment API, while developers see a DeploymentUnit, and server implementation details are contained in a DeploymentContext. Only the necessary information is exposed to a particular level of deployment's lifecycle. Components have already been mentioned as part of deployers' hierarchy handling. While top level deployment and sub-deployments are a natural representation of the deployment's structure hierarchy, components are a new VDF concept. T he idea of components is that they have a 1:1 mapping with the ControllerContexts inside the Microcontainer. See Why Components Map 1:1 with the ControllerContexts for the reasons behind this assertion. Why Components Map 1:1 with the ControllerContexts Nommage T he component unit's name is the same as the ControllerContext's name. get*Scope() and get*MetaData() return the same MDR context that will be used by the Microcontainer for that instance. IncompleteDeploymentException (IDE) In order for the IDE to print out what dependencies are missing for a deployment, it needs to know the ControllerContext names. It discovers the name by collecting the Component DeploymentUnit's names in Component Deployers that specify them, such as BeanMetaDataDeployer or the setUseUnitNam e() method in AbstractRealDeployer. 10.5. Single State Machine All Microcontainer components are handled by a single entry point, or single state machine. Deployments are no exception. 108 Chapitre 10. The Virtual D eployment Framework You can take advantage of this feature by using the jboss-dependency.xm l configuration file in your deployments. Exemple 10.5. jboss-dependency.xml <dependency xmlns="urn:jboss:dependency:1.0"> <item whenRequired="Real" dependentState="Create">TransactionManager</item> (1) <item>my-human-readable-deployment-alias</item> (2) </dependency> Note the artificial call-outs in the XML: (1) and (2). (1) shows how to describe dependency on another service. T his example requires T ransactionManager to be created before the deployment is in the 'Real' stage. (2) is a bit more complex, since you are missing additional information. By default, deployment names inside the Microcontainer are URI names, which makes typing them by hand an error prone proposition. So, in order to be able to easily declare dependence on other deployments, you need an aliasing mechanism to avoid URI names. You can add a plain text file named aliases.txt into your deployment. Each line of the file contains an alias, giving a deployment archive one or more simple names used to refer to it. 10.6. Scanning Classes for Annotations Current JEE specifications reduce the number of configuration files, but the container is now required to do most of the work using @annotations. In order to get @annotation info, containers must scan classes. T his scanning creates a performance penalty. But to reduce the amount of scanning, the Microcontainer provides another descriptor hook, by means of jboss-scanning.xm l. Exemple 10.6. jboss-scanning.xml <scanning xmlns="urn:jboss:scanning:1.0"> <path name="myejbs.jar"> <include name="com.acme.foo"/> <exclude name="com.acme.foo.bar"/> </path> <path name="my.war/WEB-INF/classes"> <include name="com.acme.foo"/> </path> </scanning> T his example shows a simple description of relative paths to include or exclude when scanning for Java Enterprise Edition version 5 and greater annotated metadata information. 109 JBoss Enterprise Application Platform 5 Guide d'utilisateur de JBoss Microcontainer Historique de révision Version 5.1.0-4 .4 00 Rebuild with publican 4.0.0 2013-10-31 Rüdiger Landmann Version 5.1.0-4 Rebuild for Publican 3.0 2012-07-18 Anthony T owns Version 5-1 Wed Sep 15 2010 Misty Stanley-Jones JBPAPP-5076 - Répare les erreurs de correspondance entre les exemples et le texte Nouveau numéro de version en accord avec les nouveaux besoins du contrôle de version Révisé pour JBoss Enterprise Application Platform 5.1.0.GA. 110