Guide de développement

JBoss Enterprise Application Platform 6.3

À utiliser dans Red Hat JBoss Enterprise Application Platform 6

Red Hat Customer Content Services

Résumé

Cet ouvrage procure des références et des exemples pour les développeurs Java EE 6 qui utilisent Red Hat JBoss Enterprise Application Platform 6, et inclut des correctifs publiés.

Chapitre 1. Introduction au développement d'applications

1.1. Introduction

1.1.1. Red Hat JBoss Enterprise Application Platform 6

Red Hat JBoss Enterprise Application Platform 6 (JBoss EAP 6) est une plate-forme middleware construite sur la base de standards ouverts et compatibles avec Java Enterprise Edition 6. Elle intègre JBoss Application Server 7 avec un clustering de haute disponibilité, une messagerie, une mise en cache distribuée et autres technologies.
JBoss EAP 6 comprend une nouvelle structure modulaire qui permet aux services d'être activés seulement si nécessaire, améliorant ainsi la vitesse de démarrage.
La console de gestion et l'interface CLI rendent la modification des fichiers de configuration XML inutile et rajoutent la capacité d'encoder et d'automatiser des tâches.
En plus, JBoss EAP 6 comprend des frameworks de développement et des API pour développer rapidement des applications de Java EE sécurisées et évolutives.

1.2. Conditions préalables

1.2.1. Familiarisez vous avec Java Enterprise Edition 6

1.2.1.1. Les Profils EE 6

Java Enterprise Edition 6 (EE 6) inclut un support pour des profils multiples, ou des sous-ensembles d'API. Les deux seuls profils que la spécification EE6 définit sont Full Profile et Web Profile.
EE 6 Full Profile inclut tous les API et toutes les spécifications incluses dans la spécification EE 6. EE 6 Web Profile inclut un sous-ensemble des APIs qui sont utiles aux développeurs web.
JBoss EAP 6 est une implémentation certifiée de Java Enterprise Edition 6 Full Profile et des spécifications Web Profile.

1.2.1.2. Web Profil de Java Enterprise Edition 6

Le Web Profil est l'un des deux profils définis dans la spécification Java Enterprise Edition 6. Il a été conçu pour le développement d'une application web. L'autre profil défini par la spécification Java Enterprise Edition 6 est le Full Profile. Pour plus d'informations, voir Section 1.2.1.3, « Java Enterprise Edition 6 Full Profile ».

Conditions préalables de Java EE 6 Web Profile

  • Java Platform, Enterprise Edition 6
  • Technologies Java Web

    • Servlet 3.0 (JSR 315)
    • JSP 2.2 et Expression Language (EL) 1.2
    • JavaServer Faces (JSF) 2.1 (JSR 314)
    • Java Standard Tag Library (JSTL) for JSP 1.2
    • Débogage du support pour les autres langages 1.0 (JSR 45)
  • Enterprise Application Technologies

    • Contexts and Dependency Injection (CDI) (JSR 299)
    • Injection de dépendance dans Java (JSR 330)
    • Enterprise JavaBeans 3.1 Lite (JSR 318)
    • Java Persistence API 2.0 (JSR 317)
    • Annotations communes de la Plateforme Java 1.1 (JSR 250)
    • Java Transaction API (JTA) 1.1 (JSR 907)
    • Bean Validation (JSR 303)

1.2.1.3. Java Enterprise Edition 6 Full Profile

La spécification Java Enterprise Edition 6 (EE 6) définit un concept de profil, et en définit deux d'entre eux dans le cadre de la spécification. En plus des items supportés dans le Java Enterprise Edition 6 Web Profile ( Section 1.2.1.2, « Web Profil de Java Enterprise Edition 6 »), le Full Profile supporte les API suivants. JBoss Enterprise Edition 6 supporte le Full Profile.

Items inclus dans EE 6 Full Profile

  • EJB 3.1 (not Lite) (JSR 318)
  • Java EE Connector Architecture 1.6 (JSR 322)
  • Java Message Service (JMS) API 1.1 (JSR 914)
  • JavaMail 1.4 (JSR 919)
  • Technologies Service Web

    • Jax-RS RESTful Web Services 1.1 (JSR 311)
    • Implémentation d'Enterprise Web Services 1.3 (JSR 109)
    • JAX-WS Java API for XML-Based Web Services 2.2 (JSR 224)
    • Java Architecture pour XML Binding (JAXB) 2.2 (JSR 222)
    • Web Services Metadata pour Java Platform (JSR 181)
    • Les API Java pour XML-based RPC 1.1 (JSR 101)
    • Les API Java pour XML Messaging 1.3 (JSR 67)
    • Java API pour les Registres XML (JAXR) 1.0 (JSR 93)
  • Technologies de Gestion et de Sécurité

    • Interface Fournisseur Service Java Authentification pour Conteneurs 1.0 (JSR 196)
    • Contrat Java Authentication Contract pour les Conteneurs 1.3 (JSR 115)
    • Déploiement Application Java EE 1.2 (JSR 88)
    • J2EE Management 1.1 (JSR 77)

1.2.2. Les modules et le système de chargement de la nouvelle classe modulaire de JBoss EAP 6.

1.2.2.1. Modules

Un module est un regroupement logique des classes utilisées pour le chargement de classes et pour la gestion des dépendances. JBoss EAP 6 identifie deux types de modules, parfois appelés modules statiques et dynamiques. Cependant, la seule différence entre les deux est la façon dont ils sont empaquetés. Tous les modules offrent les mêmes caractéristiques.
Modules statiques
Les modules statiques sont prédéfinis dans le répertoire EAP_HOME/modules/ du serveur d'applications. Chaque sous-répertoire représente un module et contient un fichier de configuration (module.xml) et tout fichier JAR requis. Le nom du module est défini dans le fichier module.xml. Tous les API fournis par le serveur de l'application sont des modules statiques, y compris les API Java EE, et les autres API comme JBoss Logging.

Exemple 1.1. Exemple de fichier module.xml

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="com.mysql">
  <resources>
    <resource-root path="mysql-connector-java-5.1.15.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>
Le nom du module, com.mysql, doit correspondre à la structure du répertoire du module.
Les modules fournis dans les distributions JBoss EAP se trouvent dans un répertoire system se trouvant lui-même dans le répertoire JBOSS_HOME/modules. Cela les rend séparés de tout module fourni par une tierce partie.
Tout produit mis en couche de Red Hat, se superposant sur JBoss EAP 6.1 ou version supérieure installera également leurs modules dans le répertoire system.
La création de modules statiques personnalisés peut être utile si plusieurs applications sont déployées sur un même serveur utilisant les mêmes bibliothèques de tierce partie. Au lieu d'un regroupement de ces bibliothèques pour chaque application, un module contenant ces bibliothèques peut être créé et installé par l'administrateur JBoss. Les applications peuvent ensuite déclarer une dépendance explicite sur les modules statiques personnalisés.
Les utilisateurs doivent s'assurer que les modules personnalisés soient installés dans le répertoire JBOSS_HOME/modules, en utilisant un répertoire par couche de modules. Cela garantit que les versions personnalisées de modules qui existent déjà dans le répertoire system soient bien chargées à la place des versions fournies. Ainsi, les modules utilisateur auront la priorité sur les modules fournis par le système.
Si vous utilisez la variable d'environnement JBOSS_MODULE_PATH pour changer les emplacements où JBoss EAP cherche les modules, le produit ira chercher dans une structure de sous-répertoire system dans un des emplacements spécifiés. Une structure de sous-répertoire system doit exister quelquepart dans les emplacements spécifiés dans JBOSS_MODULE_PATH.
Modules dynamiques
Les modules dynamiques sont créés et chargés par le serveur d'application pour chaque déploiement JAR ou WAR (ou sous-déploiement d'un EAR). Le nom d'un module dynamique est dérivé du nom de l'archive déployée. Comme les déploiements sont chargés sous forme de modules, ils peuvent configurer des dépendances et peuvent être utilisés comme dépendances par d'autres déploiements.
Les modules ne sont chargés qu'en fonction des besoins. Cela a généralement lieu quand une application est déployée avec des dépendances implicites ou explicites.

1.3. Installer l'environnement de développement

1.3.1. Télécharger et installer Red Hat JBoss Developer Studio (JDBS)

1.3.1.2. Téléchargez Red Hat JBoss Developer Studio 7.1

  1. Sélectionnez Téléchargements du menu en haut de la page.
  2. Cherchez Red Hat JBoss Developer Studio dans la liste et cliquez dessus.
  3. Sélectionnez la version appropriée et cliquez sur Télécharger.

1.3.1.3. Installer Red Hat JBoss Developer Studio 7.1

Procédure 1.1. Installer Red Hat JBoss Developer Studio 7.1

  1. Ouvrir un terminal.
  2. Aller dans le répertoire qui contient le fichier téléchargé .jar.
  3. Exécuter la commande suivante pour lancer le GUI d'installation.
    java -jar jbdevstudio-build_version.jar
  4. Cliquer sur Suivant pour commencer le processus d'installation.
  5. Sélectionner I accept the terms of this license agreement (J'accepte les conditions de licence) et cliquer sur Suivant.
  6. Ajuster le chemin d'accès de l'installation et cliquer sur Next.

    Note

    Si le dossier de chemin d'installation n'existe pas, vous verrez une invite. Cliquer alors sur Ok pour créer le dossier.
  7. Choisir une JVM, ou bien conserver la JVM sélectionnée par défaut, et cliquer sur Next.
  8. Ajouter une plateforme d'applications disponible, et cliquer sur Next.
  9. Vérifier les informations d'installation et cliquer sur Next.
  10. Cliquer sur Next une fois le processus d'installation terminé.
  11. Configurer les raccourcis bureau pour Red Hat JBoss Developer Studio et cliquer sur Next (suivant).
  12. Cliquer sur le bouton Done.

1.3.1.4. Démarrer Red Hat JBoss Developer Studio

Procédure 1.2. Commande de démarrage de Red Hat JBoss Developer Studio

  1. Ouvrir un terminal.
  2. Aller dans le répertoire d'installation.
  3. Lancer la commande suivante pour démarrer JBoss Developer Studio :
    [localhost]$ ./jbdevstudio

1.3.1.5. Ajouter le serveur de JBoss EAP en utilisant Define New Server

Ces instructions assument qu'il s'agit d'une première introduction à Red Hat JBoss Developer Studio 7.x et que vous n'avez pas encore ajouté de serveurs JBoss EAP. La procédure ci-dessous comprend l'ajout d'un serveur par l'assistant Define New Server.

Procédure 1.3. Ajouter le serveur

  1. Ouvrir l'onglet Servers. S'il n'y a pas d'onglet Servers, l'ajouter au panneau comme suit :
    1. Cliquer sur WindowShow ViewOther....
    2. Sélectionner Serveurs à partir du dossier Server et cliquer sur OK.
  2. Cliquer sur le lien create a new server ou bien cliquer à droite dans le panneau vide Serveur, et sélectionner NewServer.
    Ajouter un nouveau serveur - Aucun serveur disponible

    Figure 1.1. Ajouter un nouveau serveur - Aucun serveur disponible

  3. Étendre JBoss Enterprise Middleware et choisir JBoss Enterprise Application Platform 6.1+. Puis, cliquer sur Next pour créer le JBoss Runtime et définir le serveur. La prochaine fois que vous définirez un nouveau serveur, ce dialogue affichera un menu de sélection Server runtime environment avec la nouvelle définition de runtime.
    Définir un nouveau serveur

    Figure 1.2. Définir un nouveau serveur

  4. Saisir un nom comme "JBoss EAP 6.3 Runtime". Sous Home Directory, cliquer sur Browse puis, naviguer vers l'emplacement de l'installation de JBoss EAP. Puis, cliquer sur Next.
    Ajouter un environnement de runtime du nouveau serveur

    Figure 1.3. Ajouter un environnement de runtime du nouveau serveur

  5. Sur cet écran, vous définissez le comportement de serveur. Vous pouvez démarrer le serveur manuellement ou laisser Red Hat JBoss Developer Studio le gérer pour vous. Vous pouvez également définir un serveur distant pour le déploiement et déterminer si vous souhaitez exposer le port de gestion pour ce serveur, par exemple, si vous avez besoin de le connecter en utilisant JMX. Dans cet exemple, supposons que le serveur est local et que vous souhaitiez que Red Hat JBoss Developer Studio gère votre serveur sans que vous ayiez besoin de vérifier quoi que ce soit. Cliquer sur Next.
    Définir le nouveau comportement du serveur de JBoss

    Figure 1.4. Définir le nouveau comportement du serveur de JBoss

  6. Cet écran vous permet de configurer les projets existants pour le nouveau serveur. Puisque vous n'avez pas de projets à ce stade, cliquez sur Terminé.
    Modifier les ressources dans le nouveau serveur de JBoss

    Figure 1.5. Modifier les ressources dans le nouveau serveur de JBoss

Résultat

Le serveur de JBoss EAP est listé dans l'onglet Servers.

Le server apparaît sur la liste de serveurs

Figure 1.6. Le server apparaît sur la liste de serveurs

1.4. Exécuter votre première application

1.4.1. Télécharger les exemples de codes Quickstart

1.4.1.1. Accès aux Quickstarts

Résumé

JBoss EAP 6 contient une série d'exemples quickstart conçus pour aider les utilisateurs à commencer à rédiger des applications en utilisant les technologies Java EE 6.

Conditions préalables

Procédure 1.4. Télécharger les Quickstarts

  1. Trouver "Quickstarts" dans la liste.
  2. Cliquer sur le bouton Downloads pour télécharger un fichier ZIP contenant les exemples.
  3. Décompresser l'archive dans un répertoire de votre choix.
Résultat

Les exemples de Java EE Quickstart ont été téléchargés et décompressés. Veuillez consulter le fichier README.md dans le répertoire supérieur des archives Quickstart pour des instructions concernant le déploiement de chaque quickstart.

1.4.2. Exécuter les Quickstarts

1.4.2.1. Exécuter les quickstarts (démarrages rapides) dans Red Hat JBoss Developer Studio

Procédure 1.5. Importer les quickstarts dans Red Hat JBoss Developer Studio

Chaque quickstart est fourni avec un fichier POM (Project Object Model) qui contient des informations de projet et de configuration de quickstart. À l'aide de ce fichier POM, vous pouvez facilement importer le quickstart dans Red Hat JBoss Developer Studio.

Important

Si votre dossier de projet quickstart se situe dans l'espace de travail IDE, lorsque vous l'importez dans Red Hat JBoss Developer Studio, l'IDE génère un nom de projet non valide et un nom d'archive WAR. N'oubliez pas que votre dossier de projet quickstart doit être situé en dehors de l'espace de travail IDE avant de commencer !
  1. Démarrer Red Hat JBoss Developer Studio.
  2. À partir du menu, sélectionner FichierImporter.
  3. Dans la liste sélectionnée, choisir MavenProjets Maven existants, puis cliquer sur Suivant.
    Importer les projets Maven existants

    Figure 1.7. Importer les projets Maven existants

  4. Naviguer vers le répertoire du quickstart que vous souhaitez tester, comme helloworld, et cliquer sur OK. La zone de liste Projects verra apparaître le fichier pom.xml du projet quickstart sélectionné.
    Sélectionner les projets Maven

    Figure 1.8. Sélectionner les projets Maven

  5. Cliquer sur Terminé.

Procédure 1.6. Générer et déployer le Quickstart helloworld

Le Quickstart helloworld est un des quickstarts les plus simples et représente une bonne façon de vérifier que le serveur JBoss est configuré et exécute correctement.
  1. Si vous ne voyez pas un onglet Servers, ajoutez le au panneau comme suit :
    1. Cliquer sur WindowShow ViewOther....
    2. Sélectionner Servers à partir du dossier Server et cliquer sur OK.
  2. Cliquer à droite sur jboss-helloworld sur l'onglet Project Explorer, puis sélectionner Run As. On vous présentera une liste de choix. Sélectionner Run on Server.
    Run As - Run on Server

    Figure 1.9. Run As - Run on Server

  3. Sélectionner jboss-eap-6.3 de la liste de serveurs, et cliquer sur Next.
    Exécuter sur le serveur

    Figure 1.10. Exécuter sur le serveur

  4. L'écran suivant affiche les ressources qui sont configurées sur le serveur. Le quickstart de jboss-helloworld est configuré pour vous. Cliquer sur Finish pour déployer le quickstart.
    Modifier les ressources configurées sur le serveur

    Figure 1.11. Modifier les ressources configurées sur le serveur

  5. Vérifier les résultats.
    • Dans l'onglet Server, le statut de JBoss EAP 6.3 Runtime Server passe à [Started, Republish] .
    • L'onglet Console du serveur affiche des messages détaillant le démarrage du serveur JBoss EAP 6.3 et le déploiement du quickstart helloworld.
    • Un onglet helloworld apparaîtra avec l'URL http://localhost:8080/jboss-helloworld/HelloWorld et le texte "Hello World!".
    • Les messages suivants de la Console confirment le déploiement du fichier jboss-helloworld.war :
      JBAS018210: Register web context: /jboss-helloworld
      JBAS018559: Deployed "jboss-helloworld.war" (runtime-name : "jboss-helloworld.war")
      
      Le contexte web enregistré se rajoute à http://localhost:8080 pour fournir l'URL utilisé pour accéder à l'application qui est déployée.
  6. Pour vérifier que le Quickstart helloworld a été déployé correctement dans le serveur JBoss, ouvrir le navigateur web, et accéder à l'application dans l'URL : http://localhost:8080/jboss-helloworld

1.4.2.2. Exécuter les quickstarts par la Ligne de commande

Procédure 1.7. Générer et déployer les quickstarts par la Ligne de commande

Vous pouvez facilement générer ou déployer les démarrages rapides (quickstart) par la ligne de commande. Sachez que, lorsque vous utilisez une ligne de commande, vous devrez démarrer le serveur JBoss si nécessaire.
  1. Vérifier le fichier README.html qui se trouve dans le répertoire racine des quickstarts.
    Ce fichier contient des informations générales sur les conditions préalables de systèmes, sur la façon de configurer Maven, comment ajouter des utilisateurs, et comment exécuter les quickstarts. Lisez attentivement avant de commencer.
    Il contient également un tableau qui répertorie les quickstarts disponibles. Le tableau répertorie chaque nom de quickstart et chaque technologie dont il s'agit. Il y a une brève description pour chaque quickstart et une indication du niveau d'expérience requis pour l'installer. Pour des informations plus détaillées, cliquez sur le nom du quickstart.
    Certains quickstarts sont conçus pour améliorer ou étendre d'autres quickstarts. Ils sont indiqués dans la colonne Conditions préalables. Si un quickstart est associé à une liste de conditions préalables, vous devrez les installer avant d'utiliser le quickstart.
    Certains quickstart ont besoin de l'installation et de la configuraiton des composants optionnels. Ne pas installer ces composants à moins que le quickstart ne l'exige.
  2. Exécuter le quickstart helloworld
    Le quickstart helloworld est l'un des quickstarts les plus simples, et représente un bon moyen de vérifier si le serveur JBoss est configuré et exécute correctement. Ouvrir le fichier README.html dans la racine du quickstart helloworld. Il contient des instructions détaillés sur la façon de construire et de déployer le quickstart et accéder à l'application en cours.
  3. Exécuter d'autres quickstarts.
    Suivre les instructions dans le fichier README.html que se trouve dans le dossier racine de chaque quickstart pour exécuter l'exemple.

1.4.3. Revoir les tutoriels Quickstart

1.4.3.1. Découvrir le Quickstart HelloWorld

Résumé

Le Quickstart helloworld vous montre comment déployer un simple Servlet dans la plateforme JBoss EAP 6. La logique commerciale comprend un service fourni sous forme de bean CDI (Contexts and Dependency Injection) qui est injecté dans le Servlet. Ce Quickstart est très simple. Tout ce qu'il se contente de faire est d'imprimer "Hello World" dans une page web. C'est une bonne façon de savoir si vous avez bien configuré et démarré votre serveur.

Des instructions détaillées sur la façon de construire et de déployer ce Quickstart par la ligne de commande se trouvent dans le fichier README.html qui se trouve dans le répertoire de Quickstart helloworld. Nous vous montrons ici comment utiliser Red Hat JBoss Developer Studio pour exécuter le Quickstart. Dans cette section, nous assumons que vous avez installé le Red Hat JBoss Developer Studio, configuré Maven, importé et exécuté le Quickstart helloworld.
Conditions préalables

Procédure 1.8. Observer la structure du répertoire

Le code du Quickstart helloworld se trouve dans le répertoire QUICKSTART_HOME/helloworld. Le Quickstart helloworld comprend un Servlet et un bean CDI. Il comprend également un fichier beans.xml qui indique à JBoss EAP 6 comment trouver des beans pour cette application et comment activer le CDI.
  1. Le fichier beans.xml se trouve dans le dossier WEB-INF/ qui se trouve dans le répertoire src/main/webapp/ du Quickstart.
  2. Le répertoire src/main/webapp/ inclut également un fichier index.html qui utilise une simple réactualisation meta pour rediriger le navigateur de l'utilisateur vers le Servlet, qui se trouve à http://localhost:8080/jboss-helloworld/HelloWorld.
  3. Tous les fichiers de configuration de cet exemple se trouvent dans WEB-INF/, qui se trouve dans le répertoire src/main/webapp/ de l'exemple.
  4. Notez que le Quickstart n'a pas même besoin d'un fichier web.xml!

Procédure 1.9. Examiner le code

La déclaration de paquet et les importations ont été exclues de ces listes. La liste complète est disponible dans le code source du Quickstart.
  1. Vérifier le code HelloWorldServlet

    Le fichier HelloWorldServlet.java se trouve dans le répertoire src/main/java/org/jboss/as/quickstarts/helloworld/. Le Servlet envoie les informations dans le navigateur.
    42.  @SuppressWarnings("serial")
    43.  @WebServlet("/HelloWorld")
    44.  public class HelloWorldServlet extends HttpServlet {
    45.  
    46.      static String PAGE_HEADER = "<html><head><title>helloworld</title></head><body>";
    47.  
    48.      static String PAGE_FOOTER = "</body></html>";
    49.  
    50.      @Inject
    51.      HelloService helloService;
    52.  
    53.      @Override
    54.      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    55.          resp.setContentType("text/html");
    56.          PrintWriter writer = resp.getWriter();
    57.          writer.println(PAGE_HEADER);
    58.          writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>");
    59.          writer.println(PAGE_FOOTER);
    60.          writer.close();
    61.      }
    62.  
    63.  }
    

    Tableau 1.1. Infos HelloWorldServlet

    Ligne Note
    43 Avant Java EE 6, on utilisait un fichier XML pour enregistrer les Servlets. C'est bien plus clean. Tout ce qu'il vous reste à faire est d'ajouter l'annotation @WebServlet et de fournir un mappage vers un URL qui est utilisé pour accéder au serveur.
    46-48 Chaque page web a besoin d'HTML formé correctement. Ce Quickstart utilise les Strings statiques pour écrire les sorties minimum de l'en-tête et du pied de page.
    50-51 Ces lignes injectent le bean CDI HelloService, qui génère le message réel. Tant que nous ne changeons les API de HelloService, cette approche nous permet de modifier l'implémentation de HelloService à une date ultérieure sans changer l'affichage.
    58 Cette ligne appelle le service pour générer le message "Hello World", et l'écrire dans la requête HTTP.
  2. Vérifier le code HelloService

    Le fichier HelloService.java se trouve dans le répertoire src/main/java/org/jboss/as/quickstarts/helloworld/. Ce service est très simple. Il renvoie un message. Nul besoin d'enregistrement d'annotation ou d'XML.
    public class HelloService {
    
        String createHelloMessage(String name) {
            return "Hello " + name + "!"; 
        }
    }
    

1.4.3.2. Découvrir le Quickstart numberguess

Résumé

Ce Quickstart vous montre comment créer et déployer une simple application de JBoss EAP 6. Cette application ne persiste pas n'importe quelle information. Les informations sont affichées par JSF, et la logique métier est encapsulée dans deux beans CDI. Dans le Quickstart numberguess, vous obtenez 10 tentatives pour deviner un nombre entre 1 et 100. Après chaque tentative, on vous indique si votre estimation est trop élevée ou trop faible.

Le code du Quickstart numberguess se trouve dans le répertoire QUICKSTART_HOME/numberguess. Le Quickstart hnumberguess comprend un certain nombre de beans, des fichiers de configuration et des affichages Facelets (JSF), tout cela dans un package de module WAR.
Des instructions détaillées sur la façon de construire et de déployer ce Quickstart par la ligne de commande se trouvent dans le fichier README.html du répertoire de Quickstart numberguess. Nous vous montrons ici comment utiliser Red Hat JBoss Developer Studio pour exécuter le Quickstart. Dans cette section, nous assumons que vous avez installé le Red Hat JBoss Developer Studio, configuré Maven, importé et exécuté le Quickstart numberguess.
Conditions préalables

Procédure 1.10. Examiner les fichiers de configuration

Tous les fichiers de configuration de cet exemple se trouvent dans le répertoire WEB-INF/, qui se trouve dans le répertoire src/main/webapp/ du Quickstart.
  1. Examiner le fichier faces-config.xml.
    Ce Quickstart utilise la version de JSF 2.0 de faces-config.xml. Une version normalisée de Facelets correspond au gestionnaire d'affichage par défaut de JSF 2.0, donc il n'y a pas vraiment grand chose à configurer. JBoss EAP 6 va au-delà de Java EE ici. Il va automatiquement configurer JSF pour vous, si vous incluez ce fichier de configuration. En conséquence, la configuration ne consistera uniquement en l'élément racine :
    19. <faces-config version="2.0"
    20.    xmlns="http://java.sun.com/xml/ns/javaee"
    21.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    22.    xsi:schemaLocation="
    23.       http://java.sun.com/xml/ns/javaee>
    24.       http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
    25.      
    26. </faces-config>
    
  2. Examiner le fichier beans.xml.
    Il y a également un fichier beans.xml qui indique à JBoss EAP 6 de chercher des beans dans cette application et d'activer le CDI.
  3. Il n'y a pas de fichier web.xml
    Notez que le Quickstart n'a pas même besoin d'un fichier web.xml!

Procédure 1.11. Examiner le code JSF

JSF utilise l'extension de fichier .xhtml pour les fichiers source, mais s'occupe des vues rendues par l'extension .jsf.
  • Examiner le code home.xhtml.
    Le fichier home.xhtml se trouve dans le répertoire src/main/webapp/.
    19. <html xmlns="http://www.w3.org/1999/xhtml"
    20.    xmlns:ui="http://java.sun.com/jsf/facelets"
    21.    xmlns:h="http://java.sun.com/jsf/html"
    22.    xmlns:f="http://java.sun.com/jsf/core">
    23.
    24. <head>
    25. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    26. <title>Numberguess</title>
    27. </head>
    28.
    29. <body>
    30.    <div id="content">
    31.       <h1>Guess a number...</h1>
    32.       <h:form id="numberGuess">
    33.
    34.          <!-- Feedback for the user on their guess -->
    35.          <div style="color: red">
    36.             <h:messages id="messages" globalOnly="false" />
    37.             <h:outputText id="Higher" value="Higher!"
    38.                rendered="#{game.number gt game.guess and game.guess ne 0}" />
    39.             <h:outputText id="Lower" value="Lower!"
    40.                rendered="#{game.number lt game.guess and game.guess ne 0}" />
    41.          </div>
    42.
    43.          <!-- Instructions for the user -->
    44.          <div>
    45.             I'm thinking of a number between <span
    46.                id="numberGuess:smallest">#{game.smallest}</span> and <span
    47.                id="numberGuess:biggest">#{game.biggest}</span>. You have
    48.             #{game.remainingGuesses} guesses remaining.
    49.          </div>
    50.
    51.          <!-- Input box for the users guess, plus a button to submit, and reset -->
    52.          <!-- These are bound using EL to our CDI beans -->
    53.          <div>
    54.             Your guess:
    55.             <h:inputText id="inputGuess" value="#{game.guess}"
    56.                required="true" size="3"
    57.                disabled="#{game.number eq game.guess}"
    58.                validator="#{game.validateNumberRange}" />
    59.             <h:commandButton id="guessButton" value="Guess"
    60.                action="#{game.check}"
    61.                disabled="#{game.number eq game.guess}" />
    62.          </div>
    63.          <div>
    64.             <h:commandButton id="restartButton" value="Reset"
    65.                action="#{game.reset}" immediate="true" />
    66.          </div>
    67.       </h:form>
    68.
    69.    </div>
    70.
    71.    <br style="clear: both" />
    72.
    73. </body>
    74. </html>
    

    Tableau 1.2. Infos JSF

    Ligne Note
    36-40 Voici les messages qui peuvent être envoyés par l'utilisateur : "Higher!" et "Lower!"
    45-48 Au fur et à mesure que l'utilisateur devine, l'étendue des nombres qu'ils devinent se rétrécit. Cette phrase change pour s'assurer qu'ils connaissent la portée d'une tentative valide.
    55-58 Ce champ d'entrée est lié à une propriété de bean qui utilise une expression de valeur.
    58 On utilise une liaison de validateur pour s'assurer que l'utilisateur ne mette pas le nombre qu'il devine en dehors de la limite. Si le validateur n'était pas présent, l'utilisateur peut deviner un nombre en dehors de la limite.
    59-61 Il doit y avoir un moyen pour que l'utilisateur envoie le nombre qu'il devine au serveur. Ici, on associe une méthode d'action sur le bean.

Procédure 1.12. Examiner les fichiers de classe

Tous les fichiers source du Quickstart numberguess se trouvent dans le répertoire src/main/java/org/jboss/as/quickstarts/numberguess/. La déclaration de paquet et les importations ont été exclues de ces listings. La liste complète se trouve dans le code source du Quickstart.
  1. Vérifier le code du qualificateur Random.java.
    Un qualificateur est utilisé pour supprimer l'ambiguïté entre deux beans, qui sont tous deux admissibles pour l'injection selon leur type. Pour plus d'informations sur les qualificateurs, se référer à Section 10.2.3.3, « Utiliser un qualificateur pour résoudre une injection ambiguë »
    Le qualificateur @Random est utilisé pour injecter un nombre au hasard.
    @Target({ TYPE, METHOD, PARAMETER, FIELD })
    @Retention(RUNTIME)
    @Documented
    @Qualifier
    public @interface Random {
    
    }
  2. Vérifier le code du qualificateur MaxNumber.java.
    Le qualificateur@MaxNumber est utilisé pour injecter le nombre maximum autorisé.
    @Target({ TYPE, METHOD, PARAMETER, FIELD })
    @Retention(RUNTIME)
    @Documented
    @Qualifier
    public @interface MaxNumber {
    
    }
  3. Vérifier le code Generator.java.
    La classe Generator est chargé de créer le nombre aléatoire par une méthode de producteur. Elle indique également le nombre maximum possible par une méthode de producteur. Cette classe reste dans la portée de l'application (scoped), donc, vous n'obtenez pas un nombre différent au hasard à chaque fois.
    @SuppressWarnings("serial")
    @ApplicationScoped
    public class Generator implements Serializable {
    
        private java.util.Random random = new java.util.Random(System.currentTimeMillis());
    
        private int maxNumber = 100;
    
        java.util.Random getRandom() {
            return random;
        }
    
        @Produces
        @Random
        int next() {
            // a number between 1 and 100
            return getRandom().nextInt(maxNumber - 1) + 1;
        }
    
        @Produces
        @MaxNumber
        int getMaxNumber() {
            return maxNumber;
        }
    }
  4. Vérifier le code Game.java.
    La classe session scoped Game est le point d'entrée principal de l'application. Elle est là pour initialiser et réinitialiser le jeu, pour capturer et valider la tentative de numéro de l'utilisateur, et afin de fournir un commentaire de retour à l'utilisateur avec un FacesMessage. Elle utilise une méthode de cycle de vie de post-construction pour initialiser le jeu, en tirant un nombre au hasard du bean @Random Instance<Integer>.
    Notez l'annotation @Named dans la classe. Cette annotation est uniquement requise quand on veut rendre le bean accessible à une vue JSF via Expression Language (EL), et dans ce cas #{game}.
    @SuppressWarnings("serial")
    @Named
    @SessionScoped
    public class Game implements Serializable {
    
        /**
         * The number that the user needs to guess
         */
        private int number;
    
        /**
         * The users latest guess
         */
        private int guess;
    
        /**
         * The smallest number guessed so far (so we can track the valid guess range).
         */
        private int smallest;
    
        /**
         * The largest number guessed so far
         */
        private int biggest;
    
        /**
         * The number of guesses remaining
         */
        private int remainingGuesses;
    
        /**
         * The maximum number we should ask them to guess
         */
        @Inject
        @MaxNumber
        private int maxNumber;
    
        /**
         * The random number to guess
         */
        @Inject
        @Random
        Instance<Integer> randomNumber;
    
        public Game() {
        }
    
        public int getNumber() {
            return number;
        }
    
        public int getGuess() {
            return guess;
        }
    
        public void setGuess(int guess) {
            this.guess = guess;
        }
    
        public int getSmallest() {
            return smallest;
        }
    
        public int getBiggest() {
            return biggest;
        }
    
        public int getRemainingGuesses() {
            return remainingGuesses;
        }
    
        /**
         * Check whether the current guess is correct, and update the biggest/smallest guesses as needed. Give feedback to the user
         * if they are correct.
         */
        public void check() {
            if (guess > number) {
                biggest = guess - 1;
            } else if (guess < number) {
                smallest = guess + 1;
            } else if (guess == number) {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
            }
            remainingGuesses--;
        }
    
        /**
         * Reset the game, by putting all values back to their defaults, and getting a new random number. We also call this method
         * when the user starts playing for the first time using {@linkplain PostConstruct @PostConstruct} to set the initial
         * values.
         */
        @PostConstruct
        public void reset() {
            this.smallest = 0;
            this.guess = 0;
            this.remainingGuesses = 10;
            this.biggest = maxNumber;
            this.number = randomNumber.get();
        }
    
        /**
         * A JSF validation method which checks whether the guess is valid. It might not be valid because there are no guesses left,
         * or because the guess is not in range.
         * 
         */
        public void validateNumberRange(FacesContext context, UIComponent toValidate, Object value) {
            if (remainingGuesses <= 0) {
                FacesMessage message = new FacesMessage("No guesses left!");
                context.addMessage(toValidate.getClientId(context), message);
                ((UIInput) toValidate).setValid(false);
                return;
            }
            int input = (Integer) value;
    
            if (input < smallest || input > biggest) {
                ((UIInput) toValidate).setValid(false);
    
                FacesMessage message = new FacesMessage("Invalid guess");
                context.addMessage(toValidate.getClientId(context), message);
            }
        }
    }

1.4.4. Remplacer l'application web Welcome par défaut

JBoss EAP 6 inclut l'application Welcome, qui s'affiche quand on ouvre l'URL du serveur sur le port 8080. Vous pouvez remplacer cette application par votre propre application web, en suivant la procédure suivante.

Procédure 1.13. Remplacer l'application web Welcome par défaut par votre propre application web

  1. Désactiver l'application Welcome

    Utiliser le script d'interface CLI EAP_HOME/bin/jboss-cli.sh pour exécuter la commande suivante. Vous aurez sans doute besoin de modifier un profil de domaine géré, ou retirer une portion de la commande /profile=default du serveur autonome.
    /profile=default/subsystem=web/virtual-server=default-host:write-attribute(name=enable-welcome-root,value=false)
  2. Configurer votre application web par le contexte root.

    Afin de configurer votre application web, pour qu'elle utilise (/) comme adresse URL, modifier son fichier jboss-web.xml, qui se trouve dans le répertoire META-INF/ ou WEB-INF/. Remplacer sa directive <context-root> par une autre directive qui ressemble à ce qui suit.
    <jboss-web>
        <context-root>/</context-root>
    </jboss-web>
  3. Déployer votre application.

    Déployer votre application pour le groupe de serveurs ou le serveur que vous avez modifié lors de la première étape. L'application est maintenant disponible sur http://SERVER_URL:PORT/.

Chapitre 2. Guide Maven

2.1. Pour en savoir plus sur Maven

2.1.1. Le référentiel Maven

Apache Maven est un outil d'automation de builds distribué, utilisé en développement Java pour créer, gérer et générer des projets informatiques. Maven utilise des fichiers de configuration nommés Project Object Model, ou POM, pour définir des projets et gérer le processus de construction. Les POM décrivent le module et les dépendance du composant, et les sorties sont sous forme de fichier XML. Cela assure que le projet soit construit de façon correcte et uniforme.
Maven y parvient en utilisant un référentiel. Un référentiel Maven stocke les bibliothèques, plug-ins et autres artefacts de la build. Le référentiel public par défaut est le Maven 2 Central Repository, mais les référentiels peuvent être privés et internes à une entreprise dans le but de partager des artefacts communs au sein d'équipes de développeurs. Les référentiels sont également disponibles auprès de tierces parties. JBoss EAP 6 comprend un référentiel Maven contenant un grand nombre de prérequis que les développeurs de Java EE utilisent habituellement pour développer des applications sous JBoss EAP 6. Pour configurer votre projet pour utiliser ce référentiel, veuillez consulter Section 2.3.1, « Configurer le référentiel Maven 6 de JBoss EAP ».
On ne peut accéder aux référentiels qu'à l'aide de protocoles tels qu'http:// pour les référentiels qui se trouvent sur un serveur HHTP ou file:// pour les référentiels qui se trouvent dans un serveur de fichiers.
Pour davantage d'informations sur Maven, voir Welcome to Apache Maven.
Pour davantage d'informations sur les référentiels Maven, voir Apache Maven Project - Introduction to Repositories.
Pour davantage d'informations sur les fichiers POM de Maven, voir Apache Maven Project POM Reference et Section 2.1.2, « Le fichier POM Maven ».

2.1.2. Le fichier POM Maven

Un fichier POM (Project Object Model) est un fichier de configuration utilisé par Maven pour compiler des projets. Il s'agit d'un fichier XML contenant des informations sur un projet et sur comment le compiler, y compris l'emplacement des répertoires source, test et cible, les dépendances des projets, les réferentiels de plug-ins et les objectifs qu'il peut exécuter. Il peut également comprendre des détails supplémentaires sur le projet, comme, en autre, sa version, sa description, ses développeurs, sa liste de diffusion et sa licence. Un fichier pom.xml requiert certaines options de configuration définissant la valeur par défaut. Voir Section 2.1.3, « Conditions minimum pour un fichier POM Maven » pour plus de détails.
Le schéma pour le fichier pom.xml est disponible sur le lien http://maven.apache.org/maven-v4_0_0.xsd.
Pour plus d'informations concernant les fichiers POM, veuillez consulter Apache Maven Project POM Reference.

2.1.3. Conditions minimum pour un fichier POM Maven

Conditions minimum

Les conditions minimum pour un fichier pom.xml sont les suivantes :

  • project root
  • modelVersion
  • groupId - l'id du groupe du projet
  • artifactId - l'id de l'artifact (projet)
  • version - la version de l'artifact dans le groupe spécifié
Échantillon de fichier pom.xml

Un fichier pom.xml de base devrait resssembler à ceci :

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jboss.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

2.1.4. Le fichier des configurations de Maven

Le fichier Maven settings.xml contient des informations de configuration spécifiques à l'utilisateur pour Maven. Il contient des informations qui ne devraient pas être distribuées dans le fichier pom.xml, comme l'identité du dévelopeur, l'information sur le proxy, l'emplacement du référentiel local, et autres paramètres propres à un utilisateur.
Il y a deux endroits où on peut trouver le fichier settings.xml.
Dans l'installation Maven
Le fichier de configuration se trouve dans le répertoire M2_HOME/conf/. On les appelle les paramètres globaux. Le fichier de configuration Maven par défaut est un modèle qui peut être copié et utilisé comme point de départ pour le fichier des paramètres utilisateur.
Dans l'installation de l'utilisateur
Le fichier de configuration se trouve dans le répertoire USER_HOME/.m2/. Si les fichiers settings.xml et Maven existent tous les deux, leurs contenus seront fusionnés. S'il y a une intersection, le fichier utilisateur settings.xml aura priorité.
Voici un exemple d'un fichier settings.xml Maven :
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">  
  <profiles>
    <!-- Configure the JBoss EAP Maven repository -->
    <profile>
      <id>jboss-eap-maven-repository</id>
      <repositories>
        <repository>
          <id>jboss-eap</id>
          <url>file:///path/to/repo/jboss-eap-6.3-maven-repository</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-eap-maven-plugin-repository</id>
          <url>file:///path/to/repo/jboss-eap-6.3-maven-repository</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <!-- Optionally, make the repository active by default -->
    <activeProfile>jboss-eap-maven-repository</activeProfile>
  </activeProfiles>
</settings>
On peut trouver le schéma du fichier settings.xml à l'adresse suivante http://maven.apache.org/xsd/settings-1.0.0.xsd.

2.2. Installer le référentiel JBoss Maven et Maven

2.2.1. Télécharger et installer Maven

Si vous envisagez d'utiliser la ligne de commande Maven pour créer et déployer vos applications JBoss EAP, vous devez télécharger et installer Maven. Si vous envisagez d'utiliser Red Hat JBoss Developer Studio pour créer et déployer vos applications, vous pouvez ignorer cette procédure car Maven est distribué avec Red Hat JBoss Developer Studio.
  1. Aller dans Apache Maven Project - Download Maven et télécharger la dernière distribution de votre système d'exploitation.
  2. Voir la documentation Maven pour les informations sur le façon de télécharger et d'installer Apache Maven sur votre système d'exploitation.

2.2.2. Installer le référentiel Maven de JBoss EAP 6

Il y a trois moyens d'installer le référentiel; sur votre système de fichiers local, sur le serveur web Apache, ou par un gestionnaire de référentiel Maven.

2.2.3. Installer le référentiel Maven de JBoss EAP 6 localement

Résumé

Le référentiel de JBoss EAP 6.3 Maven est disponible en ligne. Il n'est pas donc nécessaire de le télécharger et de l'installer localement. Toutefois, si vous préférez installer le référentiel JBoss EAP Maven localement, il y a trois façons de procéder : sur votre système de fichiers local, sur le serveur Web Apache, ou avec un gestionnaire de référentiel Maven. Cet exemple couvre les étapes pour télécharger le référentiel JBoss EAP 6 Maven sur le système de fichiers local. Cette option est facile à configurer et vous permet d'obtenir d'être opérationnel rapidement sur votre ordinateur local. Il peut vous aider à vous familiariser à l'utilisation de Maven pour le développement, mais n'est pas recommandé pour les environnements de production en équipes.

Procédure 2.1. Télécharger et installer le référentiel Maven de JBoss EAP 6 sur le système de fichier local.

  1. Chercher "Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository" dans la liste.
  2. Cliquer sur le bouton Télécharger pour télécharger un fichier .zip contenant le référentiel.
  3. Décompresser le fichier dans le même répertoire sur votre système de fichiers local dans un répertoire de votre choix.
Résultat

Ceci crée un répertoire de référentiel Maven appelé jboss-eap-6.3.0.GA-maven-repository.

Important

Si vous souhaitez continuer à utiliser un référentiel local moins récent, vous devez le configurer séparemment dans le fichier de configuration settings.xml de Maven. Chaque référentiel local doit être configuré dans sa propre balise <repository>.

Important

Quand vous téléchargez un nouveau référentiel Maven, supprimer le sous-répertoire repository/ cache, qui se situe sous le répertoire .m2/ avant de tenter d'utiliser le nouveau répertoire Maven.

2.2.4. Installer le référentiel Maven de JBoss EAP 6 à utiliser avec Appache httpd

Il y a trois moyens d'installer le référentiel; sur votre système de fichiers local, sur le serveur web Apache, ou par un gestionnaire de référentiel Maven. Cet exemple porte sur les étapes de téléchargement du référentiel Maven de JBoss EAP 6 à utiliser avec Apache httpd. Cette option se prête bien aux environnements de développement inter-équipes ou multi-utilisateurs, car n'importe quel développeur qui puisse accéder à un serveur web, peut également accéder au référentiel Maven.
Conditions préalables

Il vous faut configurer Apache httpd. Voir la documentation Apache HTTP Server Project pour obtenir des instructions.

Procédure 2.2. Télécharger l'archive ZIP du référentiel Maven de JBoss EAP 6

  1. Chercher "Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository" dans la liste.
  2. Cliquer sur le bouton Télécharger pour télécharger un fichier .zip contenant le référentiel.
  3. Décompresser les fichiers dans un répertoire qui soit accessible au serveur Apache.
  4. Configurer Apache pour lui donner la permission écriture et pour permettre la navigation dans le répertoire créé.
Résultat

Cela permet à un environnement multi-utilisateurs d'accéder à un référentiel Maven sur Apache httpd.

Note

Si vous passez à une version supérieure du référentiel, veuillez noter que les artéfacts de référentiel Maven de JBoss peuvent simplement être extraits dans un référentiel Maven de produits JBoss (comme JBoss EAP 6.1.0). Une fois que l'archive de référentiel a été extraite, les artefacts peuvent être utilisés avec les paramètres existants de Maven dans ce référentiel.

2.2.5. Installer le référentiel Maven de JBoss EAP 6 en utilisant le gestionnaire de référentiels Nexus Maven

Il y a trois façons d'installer un référentiel; sur votre système de fichiers local, sur le serveur web Apache, ou à l'aide d'un gestionnaire de référentiels Maven. Cette option sera plus adaptée si vous avez déjà une licence et utilisez un gestionnaire de référentiels car vous pourrez héberger le référentiel de JBoss avec vos référentiels déjà existants. Pour obtenir plus d'informations sur les gestionnaires de référentiels Maven, voir Section 2.2.6, « Gestionnaires de référentiels Maven ».
Cet exemple couvrira les étapes d'installation du référentiel Maven de JBoss EAP 6 en utilisant le référentiel Sonatype Nexus Maven. Pour obtenir davantage d'informations, voir Sonatype Nexus: Manage Artifacts.

Procédure 2.3. Télécharger l'archive ZIP du référentiel Maven de JBoss EAP 6

  1. Chercher "Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository" dans la liste.
  2. Cliquer sur le bouton Télécharger pour télécharger un fichier .zip contenant le référentiel.
  3. Décompresser les fichiers dans un répertoire de votre choix sur le serveur qui héberge Nexus.

Procédure 2.4. Ajouter le référentiel Maven de JBoss EAP 6 en utilisant le gestionnaire de référentiels Nexus Maven

  1. Connectez-vous à Nexus en tant qu'administrateur.
  2. Sélectionner la section Repositories à partir du menu ViewsRepositories qui se trouve à gauche du gestionnaire de référentiels.
  3. Cliquer sur le menu déroulant Add..., puis sélectionner Hosted Repository.
  4. Donner un nom et une ID au nouveau référentiel.
  5. Saisir le chemin du disque qui mène au référentiel décompressé dans le champ d'emplacement Override Local Storage.
  6. Continuer si vous souhaitez que l'artéfact soit disponible dans un groupe de référentiels. Ne pas continuer avec cette procédure si ce n'est pas ce que vous souhaitez.
  7. Sélectionner le groupe de référentiels.
  8. Cliquer sur l'onglet Configure.
  9. Déplacez le nouveau référentiel JBoss Maven de la liste Available Repositories vers la liste Ordered Group Repositories sur la gauche.

    Note

    Notez que l'ordre de cette liste détermine la priorité de recherche d'artefacts Maven.
Résultat

Le référentiel est configuré par le gestionnaire de référentiels Maven Nexus.

2.2.6. Gestionnaires de référentiels Maven

Un gestionnaire de référentiel est un outil qui vous permet de gérer facilement les référentiels Maven. Les gestionnaires de référentiels sont utiles pour différentes raisons :
  • Ils donnent la possibilité de configurer des proxys entre votre organisation et des référentiels Maven à distance, ce qui offre un grand nombre d'avantages, parmi lesquels des déploiements plus rapides et plus efficaces et un meilleur niveau de contrôle sur ce qui est téléchargé par Maven.
  • Ils fournissent des destinations de déploiement pour les artefacts que vous aurez vous-même générés, permettant ainsi une collaboration entre différentes équipes de développement au sein d'une organisation.
Pour de plus amples d'informations concernant les gestionnaires de référentiels Maven, veuillez consulter la liste des gestionnaires de référentiels du projet Apache Maven Apache Maven Project - The List of Repository Managers.

Gestionnaires de référentiels Maven les plus communément utilisés

Sonatype Nexus
Voir Sonatype Nexus: Manage Artifacts pour obtenir plus d'informations sur Nexus.
Artifactory
Voir Artifactory Open Source pour obtenir plus d'informations sur Artifactory.
Apache Archiva
Voir Apache Archiva: The Build Artifact Repository Manager pour obtenir plus d'informations sur Apache Archiva.

2.3. Utiliser le référentiel Maven

2.3.1. Configurer le référentiel Maven 6 de JBoss EAP

Aperçu

Il existe deux approches pour amener Maven à utiliser le référentiel Maven de JBoss EAP 6 dans votre projet  :

  • Vous pouvez configurer les référentiels dans les paramètres globaux ou d'utilisateur de Maven.
  • Vous pouvez configurer les référentiels dans le fichier POM du projet.

Procédure 2.5. Configurer les paramètres Maven pour qu'ils utilisent le référentiel Maven de JBoss EAP 6

  1. Configurer le référentiel Maven par les paramètres Maven

    Nous vous conseillons cette dernière approche. Les paramètres Maven utilisés avec un gestionnaire de référentiel ou un référentiel sur un serveur partagé offrent un meilleur contrôle et une meilleure gestion des projets. Les paramètres offrent également la capacité d'utiliser un miroir alternatif pour rediriger toutes les demandes de recherche pour un référentiel spécifique vers un gestionnaire de référentiel sans changer les fichiers du projet. Pour plus d'informations concernant les miroirs, veuillez consulter http://maven.apache.org/guides/mini/guide-mirror-settings.html.
    Cette méthode de configuration s'applique à tous les projets Maven, tant que le fichier POM du projet ne contient pas de configuration de référentiel.
  2. Configurer le référentiel Maven pour qu'il utilise le fichier POM du projet.

    Cette méthode de configuration est généralement déconseillée. Si vous décidez de configurer des référentiels dans le fichier POM de votre projet, planifiez de manière prudente et souvenez-vous que cela pourrait ralentir votre build et que vous pourriez vous retrouver avec des artefacts ne provenant pas du référentiel attendu.

    Note

    Dans le cas d'une entreprise, où un gestionnaire de référentiel est généralement utilisé, Maven doit interroger tous les artefacts pour tous les projets en utilisant ce gestionnaire. Maven utilise tous les référentiels déclarés pour trouver les artéfacts manquants. Par conséquent, s'il ne peut trouver ce qu'il cherche, il pourra le chercher dans la centrale de référentiels (définie dans le fichier POM parent intégré). Pour remplacer cet emplacement central, vous pouvez ajouter une définition avec central de manière à ce que la centrale de référentiels devienne également votre gestionnaire de référentiel. Cela fonctionne bien avec les projets établis, mais pose un problème pour les projets vierges ou récents puisque cela crée une dépendance cyclique.
    Les fichiers POM inclus de manière transitive représentent également un problème avec ce type de configuration. Maven doit interroger ces référentiels externes pour trouver les artefacts manquants. Non seulement cela ralentira votre build, mais vous fera également perdre le contrôle sur la provenance de vos artefacts et risquera d'endommager votre build.
    Cette méthode de configuration remplace les configuration utilisateur et globales pour le projet configuré.

2.3.2. Configurer le référentiel JBoss EAP 6 Platform Maven Repository par les paramètres de configuration de Maven

Il existe deux approches pour amener Maven à utiliser le référentiel Maven de JBoss EAP 6 dans votre projet  :
  • Vous pouvez modifier les paramètres Maven. Cela amène Maven à utiliser la configuration à travers tous les projets.
  • Vous pouvez configurer le fichier POM du projet. Cela limite la configuration au projet particulier.
Cette section vous montre comment amener Maven à utiliser le référentiel JBoss EAP 6 Maven à travers tous les projets grâce aux paramètres de configuration de Maven. C'est l'approche conseillée.
Vous pouvez configurer Maven pour utiliser un référentiel de JBoss EAP 6 en ligne ou installé localement. Si vous choisissez d'utiliser le référentiel en ligne, vous pouvez utiliser un fichier de paramètres préconfigurés ou ajouter les profils de JBoss EAP 6 Maven pour le fichier de paramètres existants. Pour utiliser un référentiel local, vous devez télécharger le référentiel et configurer les paramètres pour pointer vers votre référentiel installé localement. Les procédures suivantes décrivent comment configurer Maven dans JBoss EAP 6.

Note

L'URL du référentiel dépend si le référentiel se trouve sur le système de fichiers ou sur le serveur web. Pour plus d'informations sur l'installation du référentiel, veuillez consulter : Section 2.2.2, « Installer le référentiel Maven de JBoss EAP 6 ». Voici quelques exemples pour chacune des options d'installation :
Système de fichiers
file:///path/to/repo/jboss-eap-6.x-maven-repository
Apache Web Server
http://intranet.acme.com/jboss-eap-6.x-maven-repository/
Gestionnaire de référentiel Nexus
https://intranet.acme.com/nexus/content/repositories/jboss-eap-6.x-maven-repository
Vous pouvez configurer Maven soit par les paramètres d'install globaux Maven, soit par l'install utilisateur. Ces instructions configurent les paramètres d'installation utilisateur car il s'agit de l'installation la plus commune.

Procédure 2.6. Configurer Maven par les paramètres fournis dans les exemples Quickstart

Les quickstarts de Red Hat JBoss EAP 6 contiennent un fichier settings.xml configuré pour utiliser le référentiel JBoss EAP 6 Maven en ligne. Il s'agit de l'approche la plus simple.
  1. Cette procédure remplace le fichier de configuration Maven existant, donc vous devrez sauvegarder le fichier settings.xml Maven existant.
    1. Chercher le répertoire d'installation Maven qui corresponde à votre système d'exploitation. Il se trouve normalement dans le répertoire USER_HOME/.m2/.
      • Dans Linux ou Mac, c'est: ~/.m2/
      • Pour Windows, c'est : \Documents and Settings\USER_NAME\.m2\ ou \Users\USER_NAME\.m2\
    2. Si vous avez un fichier USER_HOME/.m2/settings.xml existant, renommer le et faire une copie de sauvegarde afin de pouvoir le restaurer plus tard.
  2. Télécharger et décompresser les exemples quickstart fournis dans JBoss EAP 6. Pour plus d'informations, voir Section 1.4.1.1, « Accès aux Quickstarts »
  3. Copier le fichier QUICKSTART_HOME/settings.xml dans le répertoire USER_HOME/.m2/ .
  4. Si vous modifiez le fichier settings.xml tandis que Red  Hat JBoss Developer Studio est en cours d'exécution, suivre la procédure ci-dessous intitulée Refresh the JBoss Developer Studio User Settings.

Procédure 2.7. Modifier et configurer les paramètres Maven manuellement pour qu'ils utilisent le référentiel Maven de JBoss EAP 6 en ligne

Vous pouvez ajouter manuellement les profils JBoss EAP 6 à un fichier de paramètres Maven existant.
  1. Chercher le répertoire d'installation Maven qui corresponde à votre système d'exploitation. Il se trouve normalement dans le répertoire USER_HOME/.m2/.
    • Dans Linux ou Mac, c'est ~/.m2/
    • Pour Windows, c'est \Documents and Settings\USER_NAME\.m2\ ou \Users\USER_NAME\.m2\
  2. Si vous ne trouvez pas de fichier settings.xml, copier le fichier settings.xml du répertoire USER_HOME/.m2/conf/ dans le répertoire USER_HOME/.m2/.
  3. Copier l'XML suivant dans l'élément <profiles> du fichier.
    <!-- Configure the JBoss GA Maven repository -->
    <profile>
      <id>jboss-ga-repository</id>
      <repositories>
        <repository>
          <id>jboss-ga-repository</id>
          <url>http://maven.repository.redhat.com/techpreview/all</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-ga-plugin-repository</id>
          <url>http://maven.repository.redhat.com/techpreview/all</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
    <!-- Configure the JBoss Early Access Maven repository -->
    <profile>
      <id>jboss-earlyaccess-repository</id>
      <repositories>
        <repository>
          <id>jboss-earlyaccess-repository</id>
          <url>http://maven.repository.redhat.com/earlyaccess/all/</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-earlyaccess-plugin-repository</id>
          <url>http://maven.repository.redhat.com/earlyaccess/all/</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
    
    Copier l'XML suivant dans l'élément <activeProfiles> de settings.xml.
    <activeProfile>jboss-ga-repository</activeProfile>
    <activeProfile>jboss-earlyaccess-repository</activeProfile>
    
  4. Si vous modifiez le fichier settings.xml tandis que Red  Hat JBoss Developer Studio est en cours d'exécution, suivre la procédure ci-dessous intitulée Refresh the JBoss Developer Studio User Settings.

Procédure 2.8. Configurer les paramètres pour pouvoir utiliser un référentiel JBoss EAP installé localement

Vous pouvez modifier les paramètres pour utiliser le référentiel JBoss EAP 6 qui est installé dans le système de fichiers local.
  1. Chercher le répertoire d'installation Maven qui corresponde à votre système d'exploitation. Il se trouve normalement dans le répertoire USER_HOME/.m2/.
    • Dans Linux ou Mac, c'est ~/.m2/
    • Pour Windows, c'est \Documents and Settings\USER_NAME\.m2\ ou \Users\USER_NAME\.m2\
  2. Si vous ne trouvez pas de fichier settings.xml, copier le fichier settings.xml du répertoire USER_HOME/.m2/conf/ dans le répertoire USER_HOME/.m2/.
  3. Copier l'XML suivant dans l'élément <profiles> du fichier settings.xml. Veillez bien à changer l'<url> à l'emplacement de référentiel lui-même.
    <profile>
      <id>jboss-eap-repository</id>
      <repositories>
        <repository>
          <id>jboss-eap-repository</id>
          <name>JBoss EAP Maven Repository</name>
          <url>file:///path/to/repo/jboss-eap-6.x-maven-repository</url>
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>false</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-eap-repository-group</id>
          <name>JBoss EAP Maven Repository</name>
          <url>
          file:///path/to/repo/jboss-eap-6.x-maven-repository
          </url>
          <layout>default</layout>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>false</enabled>
            <updatePolicy>never</updatePolicy>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
    
    Copier l'XML suivant dans l'élément <activeProfiles> de settings.xml.
    <activeProfile>jboss-eap-repository</activeProfile>
    
  4. Si vous modifiez le fichier settings.xml tandis que Red  Hat JBoss Developer Studio est en cours d'exécution, suivre la procédure ci-dessous intitulée Refresh the JBoss Developer Studio User Settings.

Procédure 2.9. Réactualiser les paramètres de configuration d'utilisateur de Red Hat JBoss Developer Studio

Si vous modifiez le fichier settings.xml tandis que Red Hat JBoss Developer Studio est en cours d'exécution, vous devrez réactualiser les paramètres de configuration d'utilisateur.
  1. À partir du menu, sélectionner WindowPreferences.
  2. Dans la fenêtre Window Preferences, étendre Maven et sélectionner User Settings.
  3. Cliquer sur le bouton Update Settings (Mise à jour Configuration) pour réactualiser les configurations utilisateur de Maven dans Red Hat JBoss Developer Studio.
    Mise à jour des paramètres de configuration de l'utilisateur Maven

    Figure 2.1. Mise à jour des paramètres de configuration de l'utilisateur Maven

Important

Si votre référentiel Maven contient des artéfacts obsolètes, vous risquez de rencontrer un des messages d'erreur Maven suivants quand vous créez ou déployez votre projet :
  • Missing artifact ARTIFACT_NAME
  • [ERROR] Failed to execute goal on project PROJECT_NAME; Could not resolve dependencies for PROJECT_NAME
Pour résoudre ce problème, suppprimer la version mise en cache de votre référentiel local pour forcer un téléchargement des derniers artefacts de Maven. Le référentiel mis en cache e situe dans votre sous-répertoire ~/.m2/repository/ dans Linux, et dans le sous-répertoire %SystemDrive%\Users\USERNAME\.m2\repository\ dans Windows.

2.3.3. Configurer Maven pour utilisation dans Red Hat JBoss Developer Studio

Les artefacts et les dépendances nécessaires pour construire et déployer des applications dans Red Hat JBoss Enterprise Application Platform sont hébergés sur un référentiel public. Vous devez indiquer à Maven d'utiliser le référentiel quand vous créez vos applications. Cette section couvre les étapes de configuration de Maven si vous souhaitez créer et déployer des application avec Red Hat JBoss Developer Studio.
Maven est distribué dans Red Hat JBoss Developer Studio, donc il n'est pas nécessaire de l'installer séparément. Toutefois, vous devez configurer Maven pour qu'il puisse utiliser l'assistant Java EE Web Project pour les déploiements dans JBoss EAP. La procédure suivante montre comment configurer Maven pour utilisation avec JBoss EAP en éditant le fichier de configuration Maven au sein de Red Hat JBoss Developer Studio.

Procédure 2.10. Configurer Maven dans Red Hat JBoss Developer Studio

  1. Cliquer sur WindowPreferences, puis JBoss Tools et sélectionner JBoss Maven Integration.
    Click Window→Preferences, expand JBoss Tools and select JBoss Maven Integration.

    Figure 2.2. Panneau d'intégration de JBoss Maven dans la fenêtre Préférences

  2. Cliquer sur Configure Maven Repositories.
  3. Cliquer sur Add Repository pour configurer le référentiel de JBoss GA Tech Preview Maven. Remplir les champs de Add Maven Repository comme suit :
    1. Définir les valeurs de Profile ID, Repository ID, et Repository Name à jboss-ga-repository.
    2. Définir la valeur de Repository URL à http://maven.repository.redhat.com/techpreview/all.
    3. Cliquer sur la case Active by default pour activer le référentiel Maven.
    4. Cliquer sur OK
    Enter Maven profile and repository values.

    Figure 2.3. Ajouter le référentiel Maven - JBoss Tech Preview

  4. Cliquer sur Add Repository pour configurer le référentiel JBoss Early Access Maven. Remplir les champs de Add Maven Repository comme suit :
    1. Définir les valeurs de Profile ID, Repository ID, et Repository Name à jboss-earlyaccess-repository.
    2. Définir la valeur de Repository URL à http://maven.repository.redhat.com/techpreview/all.
    3. Cliquer sur la case Active by default pour activer le référentiel Maven.
    4. Cliquer sur OK
    Enter Maven profile and repository values.

    Figure 2.4. Ajouter le référentiel Maven - JBoss Early Access

  5. Vérifier les référentiels et cliquer sur Finish.
    Review Maven profile and repository values.

    Figure 2.5. Vérifier les référentiels Maven

  6. Le message suivant appraîtra "Are you sure you want to update the file 'MAVEN_HOME/settings.xml'?". Cliquer sur Yes pour mettre les paramètres de configuration à jour. Cliquer sur OK pour fermer la boîte de dialogue.
    Le référentiel Maven est maintenant configuré pour utilisation dans Red Hat JBoss Developer Studio

2.3.4. Configurer le référentiel JBoss EAP 6 Platform Maven Repository par le Projet POM

Il existe deux approches pour amener Maven à utiliser le référentiel Maven de JBoss EAP 6 dans votre projet  :
  • Vous pouvez modifier les paramètres Maven.
  • Vous pouvez configurer le fichier POM du projet.
Cette tâche vous montre comment configurer un projet particulier pour utiliser le référentiel JBoss EAP 6 Maven Repository en ajoutant des informations de référentiel au projet pom.xml. Cette méthode de configuration remplace les configurations Utilisateur et Globales.
Cette méthode de configuration est généralement déconseillée. Si vous décidez de configurer des référentiels dans le fichier POM de votre projet, planifiez de manière prudente et souvenez-vous que cela pourrait ralentir votre build et que vous pourriez vous retrouver avec des artefacts ne provenant pas du référentiel attendu.

Note

Dans le cas d'une entreprise, où un gestionnaire de référentiel est généralement utilisé, Maven doit interroger tous les artefacts pour tous les projets en utilisant ce gestionnaire. Maven utilise tous les référentiels déclarés pour trouver les artéfacts manquants. Par conséquent, s'il ne peut trouver ce qu'il cherche, il pourra le chercher dans la centrale de référentiels (définie dans le fichier POM parent intégré). Pour remplacer cet emplacement central, vous pouvez ajouter une définition avec central de manière à ce que la centrale de référentiels devienne également votre gestionnaire de référentiel. Cela fonctionne bien avec les projets établis, mais pose un problème pour les projets vierges ou récents puisque cela crée une dépendance cyclique.
Les fichiers POM inclus de manière transitive représentent également un problème avec ce type de configuration. Maven doit interroger ces référentiels externes pour trouver les artefacts manquants. Non seulement cela ralentira votre build, mais cela fera également perdre le contrôle sur la provenance de vos artefacts et risquera d'endommager votre build.

Note

L'URL du référentiel dépend de savoir si le référentiel se trouve sur le système de fichiers ou sur le serveur web. Pour plus d'informations sur l'installation du référentiel, veuillez consulter : Section 2.2.2, « Installer le référentiel Maven de JBoss EAP 6 ». Voici quelques exemples pour chacune des options d'installation :
Système de fichiers
file:///path/to/repo/jboss-eap-6.x-maven-repository
Apache Web Server
http://intranet.acme.com/jboss-eap-6.x-maven-repository/
Gestionnaire de référentiel Nexus
https://intranet.acme.com/nexus/content/repositories/jboss-eap-6.x-maven-repository
  1. Ouvrir le fichier pom.xml de votre projet dans un éditeur de texte.
  2. Ajouter la configuration du référentiel suivant. S'il y a déjà une configuration <repositories> qui se trouve dans le fichier, lui ajouter l'élément <repository>. Veillez bien à modifier l'<url> pour qu'il corresponde bien à l'emplacement du référentiel.
    <repositories>
       <repository>
          <id>jboss-eap-repository-group</id>
          <name>JBoss EAP Maven Repository</name>
          <url>file:///path/to/repo/jboss-eap-6.3.0-maven-repository/</url>
          <layout>default</layout>
          <releases>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
          </snapshots>
       </repository>
    </repositories>
    
  3. Ajouter la configuration de référentiel de plug-in suivante. S'il existe déjà une configuration <pluginRepositories> dans le fichier, lui ajouter l'élément <pluginRepository>.
    <pluginRepositories>
       <pluginRepository>
          <id>jboss-eap-repository-group</id>
          <name>JBoss EAP Maven Repository</name>
          <url>file:///path/to/repo/jboss-eap-6.3.0-maven-repository/</url>
          <releases>
             <enabled>true</enabled>
          </releases>
          <snapshots>
             <enabled>true</enabled>
          </snapshots>
       </pluginRepository>
    </pluginRepositories>
    

2.3.5. Gestion des dépendances du projet

Cette section décrit les fichiers POM de nomenclature (NOMENCL ou BOM de l'anglais Bills of Material) de Red Hat JBoss Enterprise Application Platform 6.
Un fichier BOM est un fichier Maven pom.xml (POM) qui indique les versions de toutes les dépendances d'exécution d'un module donné. Les dépendances de versions sont listées dans la section de gestion des dépendances du fichier.
Un projet utilise un NOMENCL en ajoutant son groupId:artifactId:version (GAV) à la section de gestion des dépendances du fichier pom.xml du projet et en spécifiant <scope>import</scope> et les valeurs de l'élément <type>pom</type>.

Note

Dans bien des cas, les dépendances des fichiers POM du projet donné utilisent le scope fourni. C'est parce que ces classes sont fournies par le serveur d'applications en cours d'exécution et qu'il n'est pas nécessaire de les empaqueter avec l'application utilisateur.

Artéfacts Maven pris en charge

Dans le cadre du processus de génération du produit, tous les composants d'exécution de JBoss EAP sont construits à partir de la source dans un environnement contrôlé. Cela permet d'assurer que les objets binaires ne contiennent pas de code malveillant, et qu'ils puissent être soutenus pour la durée de vie du produit. Ces objets peuvent être facilement identifiés par le qualificateur de version -redhat, par exemple 1.0.0-redhat-1.
L'ajout d'un artefact pris en charge pour le fichier pom.xml de configuration de build garantit que le build utilise l'artefact binaire correct pour la génération de builds locaux et pour le testing. Notez qu'un artefact avec une version -redhat ne fait pas nécessairement partie de l'API public prise en charge et peut changer à l'avenir au cours des révisions. Pour plus d'informations sur l'API public pris en charge, voir la documentation JavaDoc incluse dans la sortie.
Ainsi, pour pouvoir utiliser la version d'Hibernate prise en charge, ajouter quelque chose de similaire à votre configuration de build.
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifact>
  <version>4.2.6.Final-redhat-1</version>
  <scope>provided</scope>
</dependency>
Notez que l'exemple ci-dessus contient une valeur pour le champ <version/>. Cependant, il est recommandé d'utiliser la gestion des dépendances Maven pour configurer les versions de dépendances.

Gestion des dépendances

Maven inclut un mécanisme de gestion des versions de dépendances directes et transitives par le build. Pour obtenir des informations générales sur la gestion des dépendances, voir le projet Apache Maven Project Introduction to the Dependency Mechanism.
Utiliser une ou plusieurs dépendances JBoss prises en charge directement dans votre build ne garantit pas que toutes les dépendances transitives du build seront des artéfacts JBoss totalement pris en charge. Il est commun pour les builds Maven d'utiliser un mélange de sources d'artéfacts en provenance du référentiel central Maven, du référentiel Maven JBoss.org, ou autres référentiels Maven.
Dans le référentiel JBoss EAP Maven, il y a une NOMENCL de dépendance de gestion, qui spécifie tous les artefacts binaires de JBoss EAP pris en charge. Cette nomenclature peut être utilisée dans un build pour s'assurer que Maven donnera la priorité à des dépendances JBoss EAP prises en charge pour toutes les dépendances directes et transitives du build. En d'autres termes, les dépendances transitives seront gérées en fonction de la version de dépendance qui conviendra suivant le cas. La version de BOM correspond à celle de la version de JBoss EAP.
<dependencyManagement>
  <dependencies>
    ...
    <dependency>
      <groupId>org.jboss.bom</groupId>
      <artifactId>eap6-supported-artifacts</artifactId>
      <version>6.3.0.GA</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>

JBoss JavaEE Specs Bom de nomenclature

Le code BOM jboss-javaee-6.0 contient les JAR de spécification JAVA EE utilisés dans JBoss EAP.
Pour utiliser cette nomenclature BOM dans un projet, ajouter une dépendance au GAV qui contient la version du JSP et des JARS de l'API du serveur qui doivent être créés et déployer l'application.
L'exemple suivant utilise la version 3.0.2.Final-redhat-x de la NOMENCL jboss-javaee-6.0.
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.spec</groupId>
      <artifactId>jboss-javaee-6.0</artifactId>
      <version>3.0.2.Final-redhat-x</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.jboss.spec.javax.servlet</groupId>
    <artifactId>jboss-servlet-api_3.0_spec</artifactId>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.jboss.spec.javax.servlet.jsp</groupId>
    <artifactId>jboss-jsp-api_2.2_spec</artifactId>
    <scope>provided</scope>
  </dependency>
  ...
</dependencies>

Les nomenclatures JBoss EAP BOM et les Quickstarts

Les fichiers JBoss EAP BOM du projet jboss-bom se trouvent à l'adresse suivante https://github.com/jboss-developer/jboss-eap-boms.
Les quickstarts fournissent des exemples de cas d'utilisation primaires du référentiel Maven. Le tableau suivant dresse une liste des NOMENCL (BOM) utilisées par les quickstarts.

Tableau 2.1. Nomenclatures JBoss BOM utilisées par les Quickstarts

Id Artéfact Maven Description
jboss-javaee-6.0-with-hibernate Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant des projets communautaires Hibernate ORM, Hibernate Search et Hibernate Validator. Elle fournit également des projets d'outil tel que Hibernate JPA Model Gen et le processeur Hibernate Validator Annotation.
jboss-javaee-6.0-with-hibernate3 Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant des projets communautaires Hibernate 3 ORM, Hibernate Entity Manager (JPA 1.0) et Hibernate Validator.
jboss-javaee-6.0-with-logging Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant JBoss Logging Tools et Log4j framework.
jboss-javaee-6.0-with-osgi Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant OSGI.
jboss-javaee-6.0-with-resteasy Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant RESTEasy
jboss-javaee-6.0-with-security Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant Picketlink.
jboss-javaee-6.0-with-tools Cette nomenclature BOM s'appuie sur le profil BOM complet de Java EE, ajoutant Arquillian à l'ensemble. Elle procure également une version de JUnit et de TestNG conseillés pour l'utilisation avec Arquillian.
jboss-javaee-6.0-with-transactions Cette nomenclature BOM inclut un gestionnaire de transactions de classe mondiale. Utiliser les API JBossTS pour pouvoir profiter de toutes ses fonctionnalités.
L'exemple suivant utilise la version 6.3.0.GA de la nomemclature BOM jboss-javaee-6.0-with-hibernate.
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.bom.eap</groupId>
      <artifactId>jboss-javaee-6.0-with-hibernate</artifactId>
      <version>6.3.0.GA</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <scope>provided</scope>
  </dependency>
  ...
</dependencies>

JBoss Client BOMs

Les JBoss EAP Builds comprennent deux nomenclatures BOM : jboss-as-ejb-client-bom et jboss-as-jms-client-bom.
Les NOMENCL de clients ne créent pas de section de gestion des dépendances, ni ne définissent les dépendances. Au lieu de cela, ces nomenclatures se rajoutent à d'autres BOM et sont utilisées pour empaqueter un ensemble de dépendances nécessaires pour les cas d'utilisation de clients distants.
L'exemple suivant utilise la version 7.4.0.Final-redhat-x du client jboss-as-ejb-client-bom BOM.
<dependencies>
  <dependency>
    <groupId>org.jboss.as</groupId>
    <artifactId>jboss-as-ejb-client-bom</artifactId>
    <version>7.4.0.Final-redhat-x</version>
    <type>pom</type>
  </dependency>
  ...l
</dependencies>
Cet exemple utilise la version 7.4.0.Final-redhat-x du client BOM jboss-as-jms-client-bom.
<dependencies>
  <dependency>
    <groupId>org.jboss.as</groupId>
    <artifactId>jboss-as-jms-client-bom</artifactId>
    <version>7.4.0.Final-redhat-x</version>
    <type>pom</type>
  </dependency>
  ...
</dependencies>

Pour plus d'informations sur les dépendances Maven et sur les fichiers BOM POM, consulter Apache Maven Project - Introduction to the Dependency Mechanism.

2.4. Mise à niveau du référentiel Maven

2.4.1. Appliquer un correctif dans le répertoire Maven local

Résumé

Un référentiel Maven stocke des bibliothèques Java, des plug-ins et autres objets nécessaires pour compiler et déployer des applications JBoss EAP. Le référentiel de JBoss EAP est disponible en ligne ou en fichier ZIP téléchargé. Si vous utilisez le référentiel hébergé publiquement, les mises à jour seront appliquées automatiquement. Toutefois, si vous téléchargez et installez le repository Maven localement, vous serez chargé d'appliquer les mises à jour vous-même. Chaque fois qu'un correctif sera disponible dans JBoss EAP, un correctif correspondant sera fourni pour le référentiel JBoss EAP Maven. Ce correctif est disponible sous la forme d'un fichier ZIP incrémentiel qui est décompressé dans le référentiel local existant. Le fichier ZIP contient des nouveaux fichiers JAR et POM. Il n'écrase pas les JAR existants, ni ne les supprime, donc il n'y a aucune exigence de restauration.

Pour plus d'informations sur le Management CLI, voir le chapitre intitulé Patching and Upgrading JBoss EAP 6 de Installation Guide concernant JBoss Enterprise Application Platform 6 qui se situe sur le Portail clients à l'adresse suivante : https://access.redhat.com/site/documentation/JBoss_Enterprise_Apnplication_Platform/.
Cette tâche décrit comment appliquer les mises à jour de Maven dans votre référentiel Maven en utilisant la commande unzip.

Conditions préalables

  • Accès valide et abonnement au portail clients de Red Hat.
  • Le fichier Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository ZIP, téléchargé et installé localement.

Procédure 2.11. Mise à jour du référentiel Maven

  1. Ouvrir un navigateur et connectez-vous dans https://access.redhat.com.
  2. Sélectionner Téléchargements du menu en haut de la page.
  3. Cherchez Red Hat JBoss Enterprise Application Platform dans la liste et cliquez dessus.
  4. Sélectionner la version de JBoss EAP qui convient à partir du menu déroulant Version qui apparaît sur l'écran, puis cliquer sur Patches.
  5. Chercher Red Hat JBoss Enterprise Application Platform 6.3 CPx Incremental Maven Repository dans la liste et cliquer sur Download.
  6. Vous êtes invités à sauvegarder le fichier ZIP dans un répertoire de votre choix. Sélectionner un répertoire et sauvegardez le fichier.
  7. Chercher le chemin d'accès vers le répertoire JBoss EAP dont il s'agit dans les commandes ci-dessous, sous EAP_MAVEN_REPOSITORY_PATH, correspondant à votre système d'exploitation. Pour obtenir plus d'informations sur la façon d'installer le référentiel Maven sur le système de fichiers local, voir Section 2.2.3, « Installer le référentiel Maven de JBoss EAP 6 localement ».
  8. Décompresser le fichier de correctifs Maven dans le répertoire d'installation de JBoss EAP 6.3.x.
    • Dans Linux, ouvrir un terminal et saisir la commande suivante :
      [standalone@localhost:9999 /] unzip -o jboss-eap-6.3.x-incremental-maven-repository.zip -d EAP_MAVEN_REPOSITORY_PATH
    • Dans Windows, utiliser l'utilitaire pour extraire le fichier ZIP pour le mettre dans la racine du répertoireEAP_MAVEN_REPOSITORY_PATH .
Résultat

Le référentiel Maven installé localement est mis à jour au dernier correctif.

Chapitre 3. Chargement de classes modulaire et modules

3.1. Introduction

3.1.1. Chargement des classes de chargement et de modules

JBoss EAP 6 utilise un nouveau système de chargement de classes modulaire pour contrôler les chemins de classe d'applications déployées. Ce système offre plus de souplesse et de contrôle que le système traditionnel des chargeurs de classes hiérarchiques. Les développeurs ont un contrôle précis des classes disponibles à leurs applications et peuvent configurer un déploiement pour qu'il puisse ignorer les classes fournies par le serveur d'application en faveur de leurs propres classes.
Le nouveau chargeur de classes modulaire sépare toutes les classes Java en des groupes logiques appelés modules. Chaque module peut définir des dépendances sur d'autres modules de manière à ajouter les classes de ces modules dans leur propre chemin de classe. Le fait que chaque fichier JAR et WAR déployé soit traité comme un module permet aux développeurs de contrôler le contenu du chemin de classe de leur application en ajoutant une configuration de module à leur application.

3.1.2. Chargement des classes

Le chargement des classes est un mécanisme par lequel les classes et les ressources Java sont chargées dans l'environnement d'exécution de Java (Java Runtime Environment).

3.1.3. Modules

Un module est un regroupement logique des classes utilisées pour le chargement de classes et pour la gestion des dépendances. JBoss EAP 6 identifie deux types de modules, parfois appelés modules statiques et dynamiques. Cependant, la seule différence entre les deux est la façon dont ils sont empaquetés. Tous les modules offrent les mêmes caractéristiques.
Modules statiques
Les modules statiques sont prédéfinis dans le répertoire EAP_HOME/modules/ du serveur d'applications. Chaque sous-répertoire représente un module et contient un fichier de configuration (module.xml) et tout fichier JAR requis. Le nom du module est défini dans le fichier module.xml. Tous les API fournis par le serveur de l'application sont des modules statiques, y compris les API Java EE, et les autres API comme JBoss Logging.

Exemple 3.1. Exemple de fichier module.xml

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="com.mysql">
  <resources>
    <resource-root path="mysql-connector-java-5.1.15.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>
Le nom du module, com.mysql, doit correspondre à la structure du répertoire du module.
Les modules fournis dans les distributions JBoss EAP se trouvent dans un répertoire system se trouvant lui-même dans le répertoire JBOSS_HOME/modules. Cela les rend séparés de tout module fourni par une tierce partie.
Tout produit mis en couche de Red Hat, se superposant sur JBoss EAP 6.1 ou version supérieure installera également leurs modules dans le répertoire system.
La création de modules statiques personnalisés peut être utile si plusieurs applications sont déployées sur un même serveur utilisant les mêmes bibliothèques de tierce partie. Au lieu d'un regroupement de ces bibliothèques pour chaque application, un module contenant ces bibliothèques peut être créé et installé par l'administrateur JBoss. Les applications peuvent ensuite déclarer une dépendance explicite sur les modules statiques personnalisés.
Les utilisateurs doivent s'assurer que les modules personnalisés soient installés dans le répertoire JBOSS_HOME/modules, en utilisant un répertoire par couche de modules. Cela garantit que les versions personnalisées de modules qui existent déjà dans le répertoire system soient bien chargées à la place des versions fournies. Ainsi, les modules utilisateur auront la priorité sur les modules fournis par le système.
Si vous utilisez la variable d'environnement JBOSS_MODULE_PATH pour changer les emplacements où JBoss EAP cherche les modules, le produit ira chercher dans une structure de sous-répertoire system dans un des emplacements spécifiés. Une structure de sous-répertoire system doit exister quelquepart dans les emplacements spécifiés dans JBOSS_MODULE_PATH.
Modules dynamiques
Les modules dynamiques sont créés et chargés par le serveur d'application pour chaque déploiement JAR ou WAR (ou sous-déploiement d'un EAR). Le nom d'un module dynamique est dérivé du nom de l'archive déployée. Comme les déploiements sont chargés sous forme de modules, ils peuvent configurer des dépendances et peuvent être utilisés comme dépendances par d'autres déploiements.
Les modules ne sont chargés qu'en fonction des besoins. Cela a généralement lieu quand une application est déployée avec des dépendances implicites ou explicites.

3.1.4. Les dépendances de modules

Une dépendance de module est une déclaration qui indique qu'un module a besoin des classes d'un autre module pour pouvoir fonctionner. Les modules peuvent déclarer leurs dépendances sur un certain nombre d'autres modules. Quand le serveur d'applications charge un module, le chargeur de classes de module traite les dépendances de ce module et ajoute les classes de chaque dépendance à son chemin de classe. Si une dépendance particulière est introuvable, le module ne pourra pas charger.
Les applications déployées (JAR et WAR) sont chargées sous forme de modules dynamiques et utilisent des dépendances pour accéder aux API fournies par JBoss EAP 6.
Il y a deux types de dépendances : explicite et implicite.
Les dépendances explicites sont déclarées dans la configuration par le développeur. Les modules statiques peuvent déclarer des dépendances dans le fichier modules.xml. Les modules dynamiques peuvent avoir des dépendances déclarées dans les descripteurs de déploiement MANIFEST.MF ou jboss-deployment-structure.xml.
Les dépendances explicites peuvent être spécifiées comme étant optionnelles. Une erreur de chargement de dépendance optionnelle n'entraînera pas l'annulation d'un chargement de module. Cependant, si la dépendance est rendue disponible par la suite, elle ne sera PAS ajoutée au chemin de classe du module. Les dépendances doivent être rendues disponibles quand le module est chargé.
Des dépendances implicites sont ajoutées automatiquement par le serveur d'applications quand certaines conditions ou meta-données se trouvent dans un déploiement. Les API Java EE 6 fournies avec JBoss EAP 6 sont des exemples de modules ajoutés par détection de dépendances implicites dans les déploiements.
Les déploiements peuvent également être configurés de façon à exclure des dépendances implicites particulières. Il vous faut pour cela le fichier de déploiement jboss-deployment-structure.xml. C'est normalement le cas quand une application empaquète une version spécifique de bibliothèque que le serveur d'applications tentera d'ajouter comme dépendance implicite.
Un chemin de classe de module ne contient que ses propres classes et celles de ses dépendances immédiates. Un module n'est pas en mesure d'accéder aux classes des dépendances. Cependant, un module peut indiquer quand une dépendance explicite est exportée. Une dépendance exportée est fournie à tout module qui dépend du module qui l'exporte.

Exemple 3.2. Les dépendances de module

Le Module A dépend du Module B et le Module B dépend su Module C. Le Module A peut accéder aux classes du Module B, et le Module B peut accéder aux classes du Module C. Le Module C ne peut pas accéder aux classes du Module C à moins que :
  • Le Module A déclare une dépendance explicite sur le Module C, ou bien
  • Le Module B exporte ses dépendances sur le Module C.

3.1.5. Chargement des classes dans les déploiements

Dans le cadre du chargement de classes, tous les déploiements sont traités sous forme de modules par JBoss EAP 6. On les appelle des modules dynamiques. Les comportements de chargement de classes varient selon le type de déploiement.
Déploiement WAR
Un déploiement WAR est un simple module. Les classes du répertoire WEB-INF/lib sont considérées de la même manière que celles du répertoire WEB-INF/classes. Toutes les classes empaquetées dans le war seront téléchargées par le même chargeur de classes.
Déploiement EAR
Les déploiements EAR sont faits de plus d'un module. La définition de ces modules suit ces règles :
  1. Le répertoire lib/ du EAR est un simple module nommé le module parent.
  2. Chaque déploiement WAR du EAR est un simple module.
  3. Chaque déploiement EJB JAR du EAR est un simple module.
Les modules de sous-déploiement (les déploiements WAR et JAR dans EAR) ont une dépendance automatique sur le module parent. Cependant ils n'ont pas de dépendance automatique l'un sur l'autre. Ceci est appelé l'isolement du sous-déploiement et peut être désactivé par déploiement ou pour le serveur de toute l'application dans son ensemble.
Les dépendances explicites entre les modules de sous-déploiement peuvent être ajoutées par les mêmes moyens, tout comme pour tout autre module.

3.1.6. Précédence pour le chargement des classes

Le chargeur de classes modulaires JBoss EAP 6 utilise un système de priorité pour empêcher le chargement des conflits de classes.
Pendant le déploiement, la liste complète des packages et des classes est créée pour chaque déploiement et chacune de ses dépendances. La liste est ordonnée selon les règles de priorité de chargement de classes. Lors du chargement de classes au moment de l'exécution, le chargeur de classes recherche dans cette liste et charge la première correspondance. Cela empêche plusieurs copies des mêmes classes et packages dans le chemin d'accès de classe des déploiements d'entrer en conflit l'un avec l'autre.
Le chargeur de classe charge les classes dans l'ordre suivant, du plus élevé au plus bas :
  1. Dépendances implicites.
    Voici les dépendances qui sont ajoutées automatiquement par JBoss EAP 6, comme les API JAVA EE. Ces dépendances ont la plus haute précédence de chargeur de classe car ils contiennent des API et des fonctionnalités communes qui sont fournies par JBoss EAP 6.
    Voir Section 3.8.1, « Dépendances de modules implicites » pour obtenir des détails sur chaque dépendance implicite.
  2. Dépendances explicites
    Il s'agit de dépendances qui sont ajoutées manuellement dans la configuration de l'application. Cela peut être fait à l'aide du fichier d'application MANIFEST.MF ou du nouveau fichier du descripteur de déploiement en option de JBoss jboss-deployment-structure.xml.
    Voir Section 3.2, « Ajouter une dépendance de module explicite à un déploiement » pour apprendre comment ajouter des dépendances explicites.
  3. Ressources locales.
    Fichiers de classe empaquetées à l'intérieur du déploiement lui-même, par ex. les répertoires WEB-INF/classes ou WEB-INF/lib d'un fichier WAR.
  4. Dépendances inter-déploiement.
    Ce sont des dépendances sur les autres déploiements d'un déploiement EAR. Cela peut inclure des classes du répertoire lib du EAR ou des classes des autres jars EJB.

3.1.7. Nommage de modules dynamiques

Tous les déploiements sont chargés en tant que modules par JBoss EAP 6 et sont nommés en fonction des conventions suivantes :
  1. Les déploiements des fichiers WAR et JAR sont nommés selon le format suivant :
     deployment.DEPLOYMENT_NAME 
    Par exemple, inventory.war et store.jar auront les mêmes noms de module que deployment.inventory.war et deployment.store.jar respectivement.
  2. Les sous-déploiements des archives Enterprise sont nommés selon le format suivant :
     deployment.EAR_NAME.SUBDEPLOYMENT_NAME 
    Ainsi, le sous-déploiement reports.war, qui se trouve dans l'archive Enterprise accounts.ear, aura le nom de module du deployment.accounts.ear.reports.war.

3.1.8. jboss-deployment-structure.xml

jboss-deployment-structure.xml est un descripteur de déploiement optionnel de JBoss EAP 6. Ce descripteur de déploiement fournit un contrôle pour le chargement de classes dans le déploiement.
Le schéma XML de ce descripteur de déploiement se trouve dans EAP_HOME/docs/schema/jboss-deployment-structure-1_2.xsd

3.2. Ajouter une dépendance de module explicite à un déploiement

Cette tâche montre comment ajouter une dépendance explicite à une application. On peut ajouter des dépendances explicites de module aux applications pour ajouter les classes de ces modules au chemin de classe de l'application lors du déploiement.
Certaines dépendances sont automatiquement ajoutées aux déploiements par JBoss EAP 6. Voir Section 3.8.1, « Dépendances de modules implicites » pour plus d'informations.

Conditions préalables

  1. Vous devez déjà avoir un projet de logiciel qui fonctionne, et auquel vous souhaitez ajouter une dépendance de module.
  2. Vous devez connaître le nom du module qui est ajouté comme dépendance. Voir Section 3.8.2, « Les modules inclus » pour obtenir la liste des modules statiques inclus dans JBoss EAP 6. Si le module correspond à un autre déploiement, voir Section 3.1.7, « Nommage de modules dynamiques » pour déterminer le nom du module.
Les dépendances peuvent être configurées par deux méthodes différentes :
  1. Par l'ajout d'entrées dans le fichier MANIFEST.MF du déploiement.
  2. Par l'ajout d'entrées dans le descripteur de déploiement jboss-deployment-structure.xml.

Procédure 3.1. Par l'ajout d'une configuration de dépendance à MANIFEST.MF

Les projets Maven peuvent être configurés pour créer les entrées de dépendance requises par le fichier MANIFEST.MF. Voir Section 3.3, « Générer des entrées MANIFEST.MF en utilisant Maven ».
  1. Ajouter le fichier MANIFEST.MF

    Si le projet ne possède pas de fichier MANIFEST.MF, créer un fichier nommé MANIFEST.MF. Pour une application web (WAR), ajouter ce fichier au répertoire META-INF. Pour une archive EJB (JAR), l'ajouter au répertoire META-INF.
  2. Ajouter une entrée de dépendance

    Ajouter une entrée de dépendance au fichier MANIFEST.MF avec une liste de noms de modules de dépendance séparés par des virgules.
    Dépendances : org.javassist, org.apache.velocity
  3. Option : rendre une dépendance optionnelle

    On peut rendre une dépendance optionnelle an ajoutant optional au nom du module de l'entrée de dépendance.
    Dépendances : org.javassist optional, org.apache.velocity
  4. Option : export d'une dépendance

    On peut exporter une dépendance en ajoutant export au nom du module de l'entrée de dépendance.
    Dépendences : org.javassist, org.apache.velocity export

Procédure 3.2. Ajouter une configuration de dépendance à jboss-deployment-structure.xml

  1. Ajouter jboss-deployment-structure.xml

    Si l'application n'a pas de fichier jboss-deployment-structure.xml, créer un nouveau fichier nommé jboss-deployment-structure.xml et l'ajouter au projet. Ce fichier est un fichier XML ayant l'élément racine de <jboss-deployment-structure>.
    <jboss-deployment-structure> 
    
    </jboss-deployment-structure>
    Pour une application web (WAR), ajouter ce fichier au répertoire WEB-INF. Pour une archive EJB (JAR), l'ajouter au répertoire META-INF.
  2. Ajouter une section de dépendances

    Créer un élément <deployment> à l'intérieur de la racine du document et un élément <dependencies> également.
  3. Ajouter les éléments du module

    Dans le nœud de dépendances, ajouter un élément de module pour chaque dépendance du module. Définir l'attribut name au nom du module.
    <module name="org.javassist" />
  4. Option : rendre une dépendance optionnelle

    On peut rendre une dépendance optionnelle en ajoutant l'attribut optional à l'entrée du module, avec la valeur TRUE. La valeur par défaut de cet attribut est FALSE.
    <module name="org.javassist" optional="TRUE" />
  5. Option : export d'une dépendance

    On peut exporter une dépendance en ajoutant l'attribut optional à l'entrée du module, avec la valeur TRUE. La valeur par défaut de cet attribut est FALSE.
    <module name="org.javassist" export="TRUE" />

Exemple 3.3. jboss-deployment-structure.xml avec deux dépendances

<jboss-deployment-structure>

   <deployment>

      <dependencies>
         <module name="org.javassist" />
         <module name="org.apache.velocity" export="TRUE" />
      </dependencies>

   </deployment>

</jboss-deployment-structure>
JBoss EAP 6 ajoutera les classes à partir des modules spécifiés dans le chemin de classes de l'application quand elle sera déployée.

3.3. Générer des entrées MANIFEST.MF en utilisant Maven

Les projets Maven qui utilisent les plug-ins JAR, EJB ou WAR peuvent générer un fichier MANIFEST.MF avec une entrée Dependencies. Ce procédé ne génère pas automatiquement la liste de dépendances, il crée seulement le fichier MANIFEST.MF avec les informations spécifiées dans le fichier pom.xml.

Conditions préalables

  1. Vous devez déjà posséder un projet Maven en cours.
  2. Le projet Maven doit utiliser l'un des plug-ins JAR, EJB, ou WAR (maven-jar-plugin, maven-ejb-plugin, maven-war-plugin).
  3. Vous devez connaître le nom des dépendances de module du projet. Voir Section 3.8.2, « Les modules inclus » pour obtenir la liste des modules statiques inclus dans JBoss EAP 6. Si le module est dans un autre déploiement, voir Section 3.1.7, « Nommage de modules dynamiques » pour déterminer le nom du module.

Procédure 3.3. Générer un fichier MANIFEST.MF contenant des dépendances de module.

  1. Ajouter une configuration

    Ajouter la configuration suivante à la configuration du plug-in de l'empaquetage dans le fichier pom.xml du projet.
    <configuration>
       <archive>
          <manifestEntries>
             <Dependencies></Dependencies>
          </manifestEntries>
       </archive>
    </configuration>
  2. Liste de dépendances

    Ajouter la liste des dépendances de module dans l'élement <Dependencies>. Utiliser le même format utilisé lors de l'ajout des dépendances dans le MANIFEST.MF. Veuillez consulter Section 3.2, « Ajouter une dépendance de module explicite à un déploiement » pour plus d'informations sur ce format.
    <Dependencies>org.javassist, org.apache.velocity</Dependencies>
  3. Build de projet

    Procédez au build de projet en utilisant l'objectif d'assemblage de Maven.
    [Localhost ]$ mvn assembly:assembly
Quand un build de projet est réalisé en utilisant l'objectif d'assemblage, l'archive finale comprend un fichier MANIFEST.MF avec les dépendances de module spécifiées.

Exemple 3.4. Dépendances de module configurées dans pom.xml

L'exemple ci-dessous montre le plug-in WAR mais il fonctionne également avec les plug-ins JAR et EJB (maven-jar-plugin et maven-ejb-plugin).
<plugins>
   <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <configuration>
         <archive>
            <manifestEntries>
               <Dependencies>org.javassist, org.apache.velocity</Dependencies>
            </manifestEntries>
         </archive>
      </configuration>
   </plugin>
</plugins>

3.4. Empêcher qu'un module soit chargé implicitement

Cette tâche décrit comment configurer votre application pour en exclure une liste de dépendances de module.
Vous pouvez configurer une application déployable pour empêcher le chargement des dépendances implicites. Cela est commun quand il s'agit d'une version différente de bibliothèque ou d'un framework autre que celui qui sera fourni par le serveur d'applications comme dépendance implicite.

Conditions préalables

  1. Vous devez déjà avoir un projet informatique qui fonctionne et dont vous souhaitez exclure une dépendance.
  2. Vous devez connaître le nom du module à exclure. Voir Section 3.8.1, « Dépendances de modules implicites » pour obtenir une liste des dépendances implicites et de leurs conditions.

Procédure 3.4. Ajouter une configuration d'exclusion de dépendances de jboss-deployment-structure.xml

  1. Si l'application n'a pas de fichier jboss-deployment-structure.xml, créer un nouveau fichier intitulé jboss-deployment-structure.xml et ajoutez-le au projet. Ce fichier est un fichier XML ayant comme élément root <jboss-deployment-structure>.
    <jboss-deployment-structure>
    
    </jboss-deployment-structure>
    Pour une application web (WAR), ajouter ce fichier au répertoire WEB-INF. Pour une archive EJB (JAR), l'ajouter au répertoire META-INF.
  2. Créer un élément <deployment> dans la racine de l'élément et un <exclusions> à l'intérieur.
    <deployment>
       <exclusions>
       
       </exclusions>
    </deployment>
  3. Dans l'élément d'exclusion, ajouter un élément <module> pour chaque module à exclure. Définir l'attribut name au nom du module.
    <module name="org.javassist" />

Exemple 3.5. Exclusion de deux modules

<jboss-deployment-structure>
   <deployment>
      <exclusions>
         <module name="org.javassist" />
         <module name="org.dom4j" />
      </exclusions>
   </deployment>
</jboss-deployment-structure>

3.5. Exclure un sous-système d'un déploiement

Résumé

Ce sujet traite des étapes nécessaires pour exclure un sous-système d'un déploiement. Pour ce faire, il convient de modifier le fichier de configuration jboss-deployment-structure.xml. Le fait d'exclure un sous-système crée le même effet que de le retirer, sauf qu'il ne s'applique qu'à un seul déploiement.

Procédure 3.5. Exclure un sous-système

  1. Ouvrir le fichier jboss-deployment-structure.xml dans un éditeur de texte.
  2. Ajouter le XML suivant dans les balises de <deployment> :
    <exclude-subsystems>
      <subsystem name="SUBSYSTEM_NAME" />
    </exclude-subsystems>
  3. Enregistrer le fichier jboss-deployment-structure.xml
Résultat

Le sous-système a bien été exclus. Les processeurs d'unité de déploiement du sous-système n'exécuteront plus sur le déploiement.

Exemple 3.6. Exemple d'un fichier jboss-deployment-structure.xml.

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
  <ear-subdeployments-isolated>true</ear-subdeployments-isolated>
  <deployment>
    <exclude-subsystems>
      <subsystem name="resteasy" />
    </exclude-subsystems>
    <exclusions>
      <module name="org.javassist" />
    </exclusions>
    <dependencies>
      <module name="deployment.javassist.proxy" />
      <module name="deployment.myjavassist" />
      <module name="myservicemodule" services="import"/>
    </dependencies>
    <resources>
      <resource-root path="my-library.jar" />
    </resources>
  </deployment>
  <sub-deployment name="myapp.war">
    <dependencies>
      <module name="deployment.myear.ear.myejbjar.jar" />
    </dependencies>
    <local-last value="true" />
  </sub-deployment>
  <module name="deployment.myjavassist" >
    <resources>
     <resource-root path="javassist.jar" >
       <filter>
         <exclude path="javassist/util/proxy" />
       </filter>
     </resource-root>
    </resources>
  </module>
  <module name="deployment.javassist.proxy" >
    <dependencies>
      <module name="org.javassist" >
        <imports>
          <include path="javassist/util/proxy" />
          <exclude path="/**" />
        </imports>
      </module>
    </dependencies>
  </module>
</jboss-deployment-structure>

3.6. Utiliser le chargeur de classes par programmation dans un développement

3.6.1. Chargement des classes et des ressources par programmation dans le déploiement.

Vous pouvez trouver ou charger des classes ou des ressources par programmation dans votre code d'application. La méthode choisie dépendra d'un certain nombre de facteurs. Cette section décrit les méthodes disponibles et vous donne des instructions sur la façon de les utiliser.
Charger une classe en utilisant la méthode Class.forName()
Vous pouvez utiliser la méthode Class.forName() pour charger ou initialiser les classes par programmation. Cette méthode comprend deux signatures.
Class.forName(String className)
Cette signature ne prend en compte qu'un seul paramètre, le nom de la classe que vous devez charger. Avec cette méthode de signature, la classe est chargée par le chargeur de classes et initialise la classe nouvellement chargée par défaut.
Class.forName(String className, boolean initialize, ClassLoader loader)
Cette signature s'attend à recevoir trois paramètres : le nom de la classe, une valeur booléenne qui indique si on doit initialiser la classe, et le ClassLoader qui doit charger la classe.
La signature à trois arguments est la méthode recommandée pour charger une classe par programmation. Cette signature vous permet de contrôler si vous souhaitez que la classe cible soit initialisée pendant le chargement. Il est également plus efficace d'obtenir et de fournir le chargeur de classe parce que la JVM n'a pas besoin d'examiner la pile des appels pour déterminer quel chargeur de classe utiliser. En supposant que la classe contenant le code se nomme CurrentClass, vous pourrez obtenir le chargeur de classes de la classe par la méthode CurrentClass.class.getClassLoader().
L'exemple suivant donne un exemple de chargeur de classes qui puisse charger et initialiser la classe TargetClass :

Exemple 3.7. Fournir un chargeur de classes pour télécharger et initialiser les TargetClass.

Class<?> targetClass = Class.forName("com.myorg.util.TargetClass", true, CurrentClass.class.getClassLoader());
Chercher toutes les ressources avec un Prénom
Si vous connaissez le nom ou le chemin de la ressource, la meilleure façon de la charger directement serait d'utiliser la classe JDK ou l'API Classloader.
Charger une seule ressource
Pour charger une seule ressource qui se trouve dans le même répertoire que votre classe ou dans une autre classe de votre déploiement, vous pouvez utiliser la méthode Class.getResourceAsStream().

Exemple 3.8. Charger une seule ressource dans votre déploiement.

InputStream inputStream = CurrentClass.class.getResourceAsStream("targetResourceName");
Charger toutes les instances en une ressource unique
Pour charger toutes les instances d'une même ressource qui sont visibles par le chargeur de classe de votre déploiement, utilisez la méthode Class.getClassLoader retourne ().getResources(String resourceName), où resourceName est le chemin d'accès qualifié complet de la ressource. Cette méthode retourne une énumération de tous les objets URL pour les ressources accessibles par le chargeur de classe du prénom donné. Vous pouvez ensuite parcourir le tableau des URL pour ouvrir chaque flux à l'aide de la méthode openStream().

Exemple 3.9. Charger toutes les instances d'une ressource et itérer le résultat.

Enumeration<URL> urls = CurrentClass.class.getClassLoader().getResources("full/path/to/resource");
while (urls.hasMoreElements()) {
    URL url = urls.nextElement();
    InputStream inputStream = null;
    try {
        inputStream = url.openStream();
        // Process the inputStream
        ...
    } catch(IOException ioException) {
        // Handle the error
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {
                // ignore
            }
        }
    }
}

Note

Comme les instances URL sont chargées à partir du stockage local, il n'est pas utile d'utiliser openConnection() ou des méthodes semblables. Les flux sont plus faciles à utiliser et minimisent la complexité du code.
Charger un fichier de classe à partir d'un chargeur de classes
Si une classe a déjà été chargée, vous pouvez charger le fichier de classe qui correspond à cette classe en utilisant la syntaxe suivante :

Exemple 3.10. Charger un fichier de classe déjà chargée.

InputStream inputStream = CurrentClass.class.getResourceAsStream(TargetClass.class.getSimpleName() + ".class");
Si la classe n'a pas encore été chargée, vous devrez utiliser le chargeur de classes et en interpréter le chemin d'accès :

Exemple 3.11. Charger un fichier de classe d'une classe qui n'a pas été chargée.

String className = "com.myorg.util.TargetClass"
InputStream inputStream = CurrentClass.class.getClassLoader().getResourceAsStream(className.replace('.', '/') + ".class");

3.6.2. Itérer les ressources par programmation dans un déploiement.

La bibliothèque JBoss Modules fournit plusieurs API pour itérer toutes les ressources de déploiement. La JavaDoc de l'API de JBoss Modules se trouve à cet endroit : http://docs.jboss.org/jbossmodules/1.3.0.Final/api/. Pour utiliser des API, vous devez ajouter la dépendance suivante à MANIFEST.MF :
Dépendences: org.jboss.modules
Il est important de noter que malgré que ces API fournissent une flexibilité grandissante, ils exécuteront bien plus lentement qu'une recherche de chemin d'accès directe.
Cette section décrit un nombre de façons dont vous pouvez itérer les ressources de votre code d'application par programmation.
Lister les ressources du déploiement et de toutes les importations.
Il y a des moments où il n'est pas possible de rechercher des ressources par le chemin exact. Par exemple, le chemin d'accès exact n'est peut être pas connu ou vous devrez peut-être examiner plus d'un fichier dans un chemin d'accès donné. Dans ce cas, la bibliothèque de modules de JBoss fournit plusieurs API pour une itération de toutes les ressources de déploiement. Vous pouvez parcourir les ressources dans un déploiement en utilisant une des deux méthodes.
Itérer toutes les ressources qui se trouvent dans un seul module
La méthode ModuleClassLoader.iterateResources() itère toutes les ressources au sein de ce chargeur de classes de module. Cette méthode accepte deux arguments : le nom du répertoire de départ à rechercher et une valeur booléenne qui spécifie s'il doit agir récursivement dans les sous-répertoires.
L'exemple suivant montre comment on obtient le ModuleClassLoader ou l'itérateur pour les ressources qui se trouvent dans le répertoire bin/, récursivement dans les sous-répertoires.

Exemple 3.12. Chercher les ressources du répertoire "bin" récursivement dans les sous-répertoires.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = moduleClassLoader.iterateResources("bin",true);
L'itérateur résultant peut être utilisé pour examiner chaque ressource correspondante et pour chercher son nom et sa taille (si disponible), pour ouvrir une transmission à lire, ou pour obtenir l'URL d'une ressource.
Itérer toutes les ressources qui se trouvent dans un seul module et dans les ressources importées
La méthode Module.iterateResources() itère toutes les ressources au sein de ce chargeur de classes de module, y compris les ressources qui sont importées dans le module. Cette méthode renvoie un ensemble beaucoup plus vaste que la méthode précédente. Cette méthode requiert un argument: un filtre qui rétrécit le résultat à un modèle spécifique. Alternativement, les PathFilters.acceptAll() peuvent être fournis pour retourner l'ensemble.

Exemple 3.13. Chercher toutes les ressources de ce module, y compris les importations.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Module module = moduleClassLoader.getModule();
Iterator<Resource> moduleResources = module.iterateResources(PathFilters.acceptAll());
Chercher toutes les ressources qui correspondent à un modèle particulier
Si vous avez besoin de trouver des ressources spécifiques dans votre déploiement ou dans son importation complète, vous devrez filtrer l'itération de la ressource. Les API de filtrage de JBoss Modules peuvent vous donner plusieurs outils pour y parvenir.
Examiner toutes les dépendances
Si vous devez examiner l'ensemble des dépendances, vous pouvez utiliser le paramètre PathFilter de la méthode Module.iterateResources() pour vérifier le nom de chaque ressource et trouver une correspondance.
Examiner les dépendances de déploiement
Si vous devez chercher uniquement dans le déploiement, utilisez la méthode ModuleClassLoader.iterateResources(). Toutefois, vous devez utiliser des méthodes supplémentaires pour filtrer l'itérateur qui en résulte. La méthode PathFilters.filtered() peut fournir une vue filtrée d'un itérateur de ressources dans ce cas. La classe PathFilters comprend plusieurs méthodes statiques pour créer et composer des filtres qui remplissent des fonctions diverses, y compris la recherche de chemins dépendants ou des correspondances exactes, ou encore correspondant à un motif "glob" Ant-style.
Exemples de codes supplémentaires pour filtrer les ressources
Les exemples suivants démontrent comment filtrer les ressources sur la base de critères différents.

Exemple 3.14. Trouver tous les noms qui comprennent "messages.properties" dans votre déploiement.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/messages.properties"), moduleClassLoader.iterateResources("", true));

Exemple 3.15. Trouver tous les noms qui comprennent "messages.properties" dans votre déploiement et dans vos importations.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Module module = moduleClassLoader.getModule();
Iterator<Resource> moduleResources = module.iterateResources(PathFilters.match("**/message.properties));

Exemple 3.16. Trouver tous les fichiers qui se trouvent dans tout répertoire nommé "my-resources" de votre déploiement.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/my-resources/**"), moduleClassLoader.iterateResources("", true));

Exemple 3.17. Trouver tous les fichiers nommés "messages.properties" dans votre déploiement et dans vos importations.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Module module = moduleClassLoader.getModule();
Iterator<Resource> moduleResources = module.iterateResources(PathFilters.any(PathFilters.match("**/messages"), PathFilters.match("**/errors"));

Exemple 3.18. Trouver tous les fichiers qui se trouvent dans un package spécifique de votre déploiement.

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = moduleClassLoader.iterateResources("path/form/of/packagename", false);

3.7. Chargement de classe et sous-déploiements

3.7.1. Chargement de classes et de modules dans les EAR (Archives Enterprise)

Les EAR (Archives Enterprise) ne sont pas chargées sous forme d'un simple module à la manière des déploiements JAR ou WAR. Ils sont chargés sous forme d'une multitude de modules uniques.
Les règles suivantes déterminent quels modules existent dans un EAR.
  • Chaque sous-déploiement WAR ou EJB JAR est un module.
  • Le contenu du répertoire lib/ de la racine de l'archive EAR est un module. On l'appelle un module parent.
Ces modules possèdent le même comportement que tout autre module ayant les dépendances implicites suivantes :
  • Les sous-déploiements WAR possédent des dépendances implicites sur le module parent et sur tout sous-déploiement JAR EJB.
  • Les sous-déploiements EJB JAR possédent des dépendances implicites sur le module parent et sur tout sous-déploiement JAR EJB.

Important

Aucun sous-déploiement ne gagne une dépendance implicite sur un sous-déploiement WAR. Tout sous-déploiement peut être configuré avec des dépendances explicites sur un autre sous-déploiement comme dans n'importe quel autre module.
Les dépendances implicites décrites ci-dessus ont lieu car JBoss EAP 6 a l'isolement de chargeur de classe de sous-déploiement désactivé par défaut.
Tout isolement de chargeur de classe de sous-déploiement peut être activé si une stricte compatibilité est nécessaire. Ceci peut être activé pour un simple déploiement EAR ou pour tous les déploiements EAR. La spécification Java EE 6 recommande que les applications portables ne reposent pas sur des sous-déploiements pouvant accéder les uns aux autres, à moins que les dépendances soient explicitement déclarées comme entrées Class-Path dans le fichier MANIFEST.MF de chaque sous-déploiement.

3.7.2. Isolement du chargeur de classes d'un sous-déploiement

Chaque sous-déploiement d'EAR (Archive Enterprise) est un module dynamique possédant son propre chargeur de classe. Par défaut, un sous-déploiement peut accéder aux ressources d'autres sous-déploiements.
Si un sous-déploiement ne doit pas accéder aux ressources d'autres sous-déploiements (une isolation de sous-déploiement est alors requise), alors cela pourra être activé.

3.7.3. Désactiver l'isolement du chargeur de classes d'un sous-déploiement dans un EAR

Cette tâche vous montre comment faire pour désactiver l'isolement du chargeur de classes de sous-déploiement dans un déploiement EAR, à l'aide d'un descripteur de déploiement spécial EAR. Cela ne nécessite des modifications au serveur d'applications et n'affecte pas les autres déploiements.

Important

Même quand l'isolement d'un chargeur de classes de sous-déploiement est désactivé, il est possible d'ajouter un déploiement WAR comme dépendance.
  1. Ajouter le fichier du descripteur de déploiement

    Ajouter le fichier de descripteur de déploiement jboss-deployment-structure.xml au répertoire META-INF du EAR s'il n'existe pas déjà et ajouter le contenu suivant :
    <jboss-deployment-structure>
    
    </jboss-deployment-structure>
  2. Ajouter l'élément <ear-subdeployments-isolated>

    Ajouter l'élément <ear-subdeployments-isolated> au fichier jboss-deployment-structure.xml s'il n'existe pas déjà dans le contenu de false.
    <ear-subdeployments-isolated>false</ear-subdeployments-isolated>
Résultat  :

L'isolement de chargeur de classe de sous-déploiement sera maintenant désactivé pour le déploiement de cet EAR. Cela signifie que les sous-déploiements du EAR auront des dépendances automatiques sur chacun des sous-déploiements non-WAR.

3.8. Référence

3.8.1. Dépendances de modules implicites

Le tableau suivant liste les modules qui sont ajoutés automatiquement au déploiement en tant que dépendances et les conditions qui déclenchent la dépendance.

Tableau 3.1. Dépendances de modules implicites

Sous-système responsable de l'ajout de la dépendance Dépendances qui sont toujours ajoutées Dépendances qui sont ajoutées conditionnellement Conditions qui déclenchent l'ajout de dépendances
Serveur principal
  • javax.api
  • sun.jdk
  • org.jboss.vfs
EE subsystem
  • javaee.api
EJB 3 subsystem
  • javaee.api
La présence d'un ficher ejb-jar.xml à des emplacements valides dans le déploiement, tel que mentionné dans les spécifications Java EE 6.
La présence d'EJB basés-annotations, comme : @Stateless, @Stateful, @MessageDriven
Sous-système JAX-RS (RESTEasy)
  • javax.xml.bind.api
  • org.jboss.resteasy.resteasy-atom-provider
  • org.jboss.resteasy.resteasy-cdi
  • org.jboss.resteasy.resteasy-jaxrs
  • org.jboss.resteasy.resteasy-jaxb-provider
  • org.jboss.resteasy.resteasy-jackson-provider
  • org.jboss.resteasy.resteasy-jsapi
  • org.jboss.resteasy.resteasy-multipart-provider
  • org.jboss.resteasy.async-http-servlet-30
La présence d'annotations JAX-RS dans le déploiement.
Sous-système JCA
  • javax.resource.api
  • javax.jms.api
  • javax.validation.api
  • org.jboss.logging
  • org.jboss.ironjacamar.api
  • org.jboss.ironjacamar.impl
  • org.hibernate.validator
Le déploiement d'une archive d'adaptateur de ressources (RAR).
Sous-système JPA (Hibernate)
  • javax.persistence.api
  • javaee.api
  • org.jboss.as.jpa
  • org.hibernate
La présence de l'annotation @PersistenceUnit ou @PersistenceContext, <persistence-unit-ref> ou encore d'un élément <persistence-context-ref> dans un descripteur de déploiement.
JBoss EAP 6 mappe les noms du fournisseur de persistance aux noms du module. Si vous nommez un fournisseur particulier dans le fichier persistence.xml, une dépendance sera ajoutée au module qui convient. Si ce comportement ne convient pas, vous pourrez l'exclure par l'intermdiaire d'un fichier jboss-deployment-structure.xml.
Sous-système de journalisation
  • org.jboss.logging
  • org.apache.log4j
  • org.apache.commons.logging
  • org.slf4j
  • org.jboss.logging.jul-to-slf4j-stub
Ces dépendances sont toujours ajoutées à moins que l'attribut add-logging-api-dependencies soit défini à false.
Sous-système SAR
  • org.jboss.logging
  • org.jboss.modules
Le déploiement d'une archive SAR.
Sous-système de sécurité
  • org.picketbox
Sous-système Web
  • javaee.api
  • com.sun.jsf-impl
  • org.hibernate.validator
  • org.jboss.as.web
  • org.jboss.logging
Le déploiement d'une archive WAR. JavaServer Faces (JSF) est ajouté uniquement si utilisé.
Sous-système de Services Web
  • org.jboss.ws.api
  • org.jboss.ws.spi
Sous-système Weld (CDI)
  • javax.persistence.api
  • javaee.api
  • org.javassist
  • org.jboss.interceptor
  • org.jboss.as.weld
  • org.jboss.logging
  • org.jboss.weld.core
  • org.jboss.weld.api
  • org.jboss.weld.spi
La présence d'un fichier beans.xml dans le déploiement.

3.8.2. Les modules inclus

Un tableau énumérant les modules JBoss EAP 6 inclus et indiquant s'ils sont pris en charge se trouve dans le portail clients à https://access.redhat.com/articles/1122333.

3.8.3. Référence de descripteur de déploiement de structure de déploiement de JBoss

Les tâches principales qui peuvent être effectuées par ce descripteur de déploiement sont les suivantes :
  • Définition des dépendances de modules explicites.
  • Empêcher les dépendances implicites spécifiques de charger.
  • Définir des modules supplémentaires à partir des ressources de ce déploiement.
  • Modifier le comportement de l'isolation du sous-déploiement dans ce déploiement EAR.
  • Ajouter des racines de ressources supplémentaires à un EAR.

Chapitre 4. Valves

4.1. Valves

Une valve est une classe Java insérée dans le pipeline de traitement de demande d'une application. Elle est insérée dans le pipeline avant les filtres servlet. Les valves peuvent apporter des modifications à la demande avant de les passer ou d'effectuer tout autre traitement comme l'authentification ou annuler la demande.
Les valves peuvent être configurées au niveau du serveur ou au niveau de l'application. La seule différence est la façon dont elles sont configurées ou empaquetées.
  • Les valves globales sont configurées au niveau serveur et s'appliquent à toutes les applications déployées dans le serveur. Les instructions sur la façon de configurer les valves globales se trouvent dans le guide Administration and Configuration Guide de JBoss EAP.
  • Les valves configurées au niveau de l'application sont empaquetées dans le déploiement de l'application et n'affectent que l'application en question. Des instructions sur la façon de configurer des valves au niveau de l'application se trouvent dans le guide Development Guide de JBoss EAP.
Les versions 6.1.0 et supérieures prennent en charge les valves globales.

4.2. Valves globales

Une valve globale est une valve insérée dans le pipeline de traitement de requête de toutes les applications déployées. Une valve est rendue globale lorsqu'elle est mise en paquetage et qu'elle est installée comme module static dans JBoss EAP 6. Les valves globales sont configurées dans le sous-système web.
Seules les versions 6.1.0 et supérieures prennent en charge les valves globales.
Pour obtenir des instructions sur la façon de configurer les valves globales, voir le chapitre qui s'intitule Global Valves dans le guide Administration and Configuration Guide for JBoss EAP.

4.3. Les valves d'authentification

Une valve d'authentification est une valve qui authentifie les informations d'identification d'une requête. Cette valve est une sous-classe de org.apache.catalina.authenticator.AuthenticatorBase et elle remplace la méthode authenticate(Request request, Response response, LoginConfig config).
Elle peut être utilisée pour implémenter des schémas d'authentification supplémentaires.

4.4. Configurer une application web pour utiliser une valve.

Les valves qui ne sont pas installées en tant que valves globales doivent être incluses avec votre application et configurées dans le descripteur de déploiement jboss-web.xml.

Important

Les valves globales sont appliquées automatiquement à toutes les applications qui sont déployées. Pour obtenir des instructions sur la façon de configurer les valves globales, voir le chapitre qui s'intitule Global Valves dans le guide Administration and Configuration Guide for JBoss EAP.

Conditions préalables

  • La valve doit être créée et incluse dans le chemin de classe de l'application. Pour ce faire, il convient de l'inclure dans le fichier WAR de l'application ou dans tout autre module ajouté comme dépendance. Les exemples de tels modules comprennent un module statique installé sur le serveur ou un fichier JAR dans le répertoire lib/ d'une archive EAR si le WAR est déployé dans un EAR.
  • L'application doit inclure un descripteur de déploiement de jboss-web.xml.

Procédure 4.1. Configurer une application pour une valve locale.

  1. Configuration d'une valve

    Créer un élément valve contenant l'élément enfant class-name au fichier jboss-web.xml de l'application. Le class-name est le nom de la classe de valve.
    <valve>
          <class-name>VALVE_CLASS_NAME</class-name>
    </valve>

    Exemple 4.1. Exemple d'élément de valve configuré dans le fichier jboss-web.xml

    <valve>
          <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name>
    </valve>
  2. Configurer une valve personnalisée

    Si la valve possède des paramètres configurables, ajouter un élément enfant param à l'élément valve pour chaque paramètre, en spécifiant leur nom et leur valeur param-name et param-value.

    Exemple 4.2. Exemple d'élément de valve personnalisé configuré dans le fichier jboss-web.xml

    <valve>   
        <class-name>org.jboss.web.tomcat.security.GenericHeaderAuthenticator</class-name>  
        <param>  
            <param-name>httpHeaderForSSOAuth</param-name>  
            <param-value>sm_ssoid,ct-remote-user,HTTP_OBLIX_UID</param-value>  
        </param>  
        <param>  
            <param-name>sessionCookieForSSOAuth</param-name>  
            <param-value>SMSESSION,CTSESSION,ObSSOCookie</param-value>  
        </param>  
    </valve>
Une fois l'application déployée, la valve sera autorisée pour cette application avec la configuration spécifiée.

Exemple 4.3. Configuration valve jboss-web.xml

<valve>
    <class-name>org.jboss.samplevalves.RestrictedUserAgentsValve</class-name>
    <param>  
        <param-name>restrictedUserAgents</param-name>
        <param-value>^.*MS Web Services Client Protocol.*$</param-value>
    </param>
 </valve>

4.5. Configurer une application web pour qu'elle utilise une valve d'authentification

Afin de configurer une application pour utiliser une valve d'authentification, il convient d'installer et de configurer la valve (locale à l'application ou en tant que valve globale) et de configurer le descripteur de déploiement web.xml de l'application. Dans le cas le plus simple, la configuration web.xml revient à utiliser une authentification BASIC sauf que l'élément enfant auth-method de login-config est défini sous le nom de la valve qui effectue la configuration.

Conditions préalables

  • La valve d'authentification devrait déjà être créée.
  • Si la valve d'authentification est une valve globale, cette dernière devrait déjà être installée et configurée, et il convient de connaître le nom sous lequel elle a été configurée.
  • Informez-vous du nom de domaine de la zone de sécurité que l'application utilise.
Si vous ne savez pas quel nom de domaine de la zone de sécurité ou la valve à utiliser, informez-vous auprès de l'administrateur de votre serveur.

Procédure 4.2. Configurer une application pour utiliser une valve d'authentification

  1. Configurer la valve

    L'utilisation d'une valve locale nécessite une configuration dans le descripteur de déploiement jboss-web.xml des applications. Voir Section 4.4, « Configurer une application web pour utiliser une valve. ».
    Cela n'est pas nécessaire pour l'utilisation d'une valve globale.
  2. Ajouter une configuration de sécurité à web.xml.

    Ajouter la configuration de sécurité au fichier web.xml pour votre application en utilisant les éléments standards tels que la contrainte de sécurité, la configuration de l'identifiant et le rôle de la sécurité. Dans l'élément de configuration de l'identifiant, définir la valeur de la méthode d'identification en tant que nom de la valve d'identification. L'élément de nom de la zone doit également être défini en tant que nom de la zone de sécurité de JBoss utilisée par l'application.
    <login-config>
       <auth-method>VALVE_NAME</auth-method>
       <realm-name>REALM_NAME</realm-name>
    </login-config>
Quand l'application est déployée, l'authentification des requêtes est gérée par la valve d'authentification configurée.

4.6. Créer une valve personnalisée

Une Valve est une classe Java qui est insérée dans le pipeline de traitement de requête d'une application avant les filtres de servlet de l'application. Elle peut être utilisée pour modifier la demande ou pour un autre comportement. Cette tâche vous montre les étapes de base requises pour créer une valve.

Procédure 4.3. Créer une valve personnalisée

  1. Créer la classe Valve

    Créer une sous-classe de org.apache.catalina.valves.ValveBase.
    package org.jboss.samplevalves;
    
    import org.apache.catalina.valves.ValveBase;
    import org.apache.catalina.connector.Request;
    import org.apache.catalina.connector.Response;
    
    public class RestrictedUserAgentsValve extends ValveBase {
    
    }
  2. Mettre en place la méthode d'invocation

    La méthode invoke() est utilisée lorsque la valve est exécutée dans la pipeline. Les objets de requête et de réponse sont considérés comme des paramètres. Effectuer tout procédé et modification de requête et réponse ici.
    public void invoke(Request request, Response response)
    {
    
    }
  3. Invoquer la prochaine étape de pipeline

    La dernière étape que la méthode d'invocation doit effectuer est d'invoquer l'étape suivante de la pipeline et de transmettre les objets de requête et de réponse modifiés, en utilisant la méthode getNext().invoke().
    getNext().invoke(request, response);
  4. Option : indiquer les paramètres

    Si la valve doit être configurable, activer cette option en ajoutant un paramètre. Pour ce faire, il convient d'ajouter une variable d'instance et une méthode setter pour chaque paramètre.
    private String restrictedUserAgents = null;
    
    public void setRestricteduserAgents(String mystring) 
    {
       this.restrictedUserAgents = mystring;
    }

Exemple 4.4. Échantillon de valve personnalisée

package org.jboss.samplevalves;

import java.io.IOException;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import org.apache.catalina.valves.ValveBase;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;

public class RestrictedUserAgentsValve extends ValveBase 
{
    private String restrictedUserAgents = null;

    public void setRestrictedUserAgents(String mystring) 
    {
        this.restrictedUserAgents = mystring;
    }

    public void invoke(Request request, Response response) throws IOException, ServletException 
    {
      String agent = request.getHeader("User-Agent");
      System.out.println("user-agent: " + agent + " : " + restrictedUserAgents);
      if (Pattern.matches(restrictedUserAgents, agent)) 
      {
         System.out.println("user-agent: " + agent + " matches: " + restrictedUserAgents);
         response.addHeader("Connection", "close");
      }
      getNext().invoke(request, response);
    }
}

Chapitre 5. La journalisation pour les développeurs

5.1. Introduction

5.1.1. Journalisation

La journalisation consiste à l'enregistrement d'une série de messages d'une application dans un fichier-journal des activités de l'application.
Les messages de journalisation fournissent des informations importantes pour les développeurs lors du débogage d'une application et pour les administrateurs système quand au maintien des applications de production.
La plupart des frameworks de journalisation de Java incluent également des informations comme l'heure et l'origine du message.

5.1.2. Frameworks de journalisations d'applications pris en charge par JBoss LogManager

JBoss LogManager prend en charge les frameworks de journalisation suivants :

5.1.3. Niveaux de journalisation

Les niveaux de journalisation sont des ensembles ordonnés de valeurs énumérées qui indiquent la nature et la sévérité d'un message de journalisation. Le niveau d'un message de journalisation donné est indiqué par le développeur par des méthodes qui conviennent dans un framework de journalisation particulier pour envoyer le message.
JBoss EAP 6 accepte tous les niveaux de journalisation utilisés par les frameworks de journalisation de l'application prise en charge. Les six niveaux de journalisation les plus utilisés sont (dans l'ordre croissant) : TRACE, DEBOG, INFO, ATTENTION, ERREUR et FATAL.
Les niveaux de journalisation sont utilisés par des catégories et des gestionnaires de journalisation pour limiter les messages dont ils sont responsables. Chaque niveau de journalisation possède une valeur numérique qui indique son ordre par rapport à d'autres niveaux de journalisation. Les catégories et gestionnaires de journalisation correspondent à un certain niveau de journalisation, et ils traitent les messages de journalisation du même niveau ou d'un niveau supérieur uniquement. Par exemple, un gestionnaire de journalisation du niveau ATTENTION enregistrera uniquement les messages des niveaux ATTENTION, ERREUR et FATAL.

5.1.4. Niveaux de journalisation pris en charge

Tableau 5.1. Niveaux de journalisation pris en charge

Niveau de journalisation Valeur Description
FINESSE MAX 300
-
PLUS FIN 400
-
TRACE 400
Utilisé pour des messages qui fournissent des informations détaillées sur l'état d'exécution d'une application. Les messages de journalisation TRACE sont habituellement capturés uniquement lors du débogage d'une application.
DEBOG 500
Utilisé pour des messages qui indiquent des demandes individuelles de progrès ou des activités d'une application. Les messages de journalisation DEBUG ne sont habituellement capturés que lors du débogage d'une application.
FINESSE 500
-
CONFIG 700
-
INFO 800
Utilisé pour des messages qui indiquent la progression globale de l'application. Souvent utilisé pour le démarrage de l'application, la fermeture et autres événements majeurs de cycle de vie.
AVERTISSEMENT 900
Utilisé pour indiquer une situation qui n'est pas en erreur, mais n'est pas considérée comme idéale. Peut indiquer des circonstances qui peuvent entraîner des erreurs dans le futur.
ATTENTION 900
-
ERREUR 1000
Utiliser pour indiquer une erreur qui s'est produite et qui puisse empêcher l'activité actuelle ou la demande de se remplir, mais qui n'empêchera pas l'application d'exécuter.
SÉVÈRE 1000
-
FATALE 1100
Utiliser pour indiquer les événements qui pourraient entraîner des défaillances de services critiques ou la fermeture de l'application, ou qui pourraient entraîner la fermeture de la plateforme JBoss EAP 6.

5.1.5. Emplacements de fichiers de journalisation par défaut

Il s'agit des fichiers de journalisation qui ont été créés pour les configurations de journalisation par défaut. La configuration par défaut écrit des fichiers de journalisation du serveur à l'aide de gestionnaires de journaux périodiques.

Tableau 5.2. Fichier de journalisation par défaut d'un serveur autonome

Fichier journal Description
EAP_HOME/standalone/log/server.log
Journal du serveur. Contient les messages de journalisation de serveur, dont les messages de démarrage de serveur.
EAP_HOME/standalone/log/gc.log
Journalisation de Garbage collection. Contient des informations sur tous les nettoyagess de mémoire.

Tableau 5.3. Fichiers de journalisation par défaut d'un domaine géré

Fichier journal Description
EAP_HOME/domain/log/host-controller.log
Journal d'amorçage du contrôleur hôte. Contient les messages de journalisation liés au démarrage du contrôleur hôte.
EAP_HOME/domain/log/process-controller.log
Journal d'amorçage du contrôleur de processus. Contient les messages de journalisation liés au démarrage du contrôleur de processus.
EAP_HOME/domain/servers/SERVERNAME/log/server.log
Le journal du serveur pour le serveur nommé. Contient les messages de journalisation de ce serveur, dont les messages de démarrage de serveur.

5.2. Journalisation dans le JBoss Logging Framework

5.2.1. JBoss Logging

JBoss Logging est le framework de journalisation d'applications inclus dans JBoss EAP 6
JBoss Logging procure un moyen facile d'ajouter une journalisation à une application. Vous ajoutez du code à votre application qui utilise le framework pour envoyer des messages de journalisation dans un format défini. Lorsque l'application est déployée sur un serveur d'applications, ces messages peuvent être capturés par le serveur et affichées ou écrits dans les fichiers selon la configuration du serveur.

5.2.2. Fonctionnalités de JBoss Logging

  • Fournit un enregistreur d'événements de saisie ("typed") facile d'utilisation.
  • Support total pour l'internationalisation et la localisation. Les traducteurs travaillent sur des lots de messages dans des fichiers de propriétés, tandis que les développeurs peuvent travailler sur des interfaces et des annotations.
  • Outils « buid-time » pour générer les enregistreurs d'événements en saisie pour la production, et la génération runtime d'enregistreurs d'événements pour le développement.

5.2.3. Ajouter une journalisation à une application par JBoss Logging

Pour journaliser des messages à partir de votre application, créer un objet de créateur de journal (org.jboss.logging.Logger) et utiliser la méthode appropriée de cet objet. Cette tâche décrit les étapes nécessaires pour ajouter un support pour ceci à votre application.

Conditions préalables

Vous devez remplir les conditions suivantes avant de continuer cette tâche :
  • Si vous utilisez Maven en tant que système de build, il convient que le projet soit déjà configuré pour inclure le référentiel JBoss Maven. Voir Section 2.3.2, « Configurer le référentiel JBoss EAP 6 Platform Maven Repository par les paramètres de configuration de Maven »
  • Les fichiers JAR de JBoss Logging doivent se trouver dans le chemin de build d'application. La façon dont vous procédrez dépendra de votre décision de générer votre application avec Red Hat JBoss Developer Studio ou avec Maven.
    • Quand vous générez avec Red Hat JBoss Developer Studio vous pouvez procéder en sélectionnant Project -> Properties à partir du menu Red Hat JBoss Developer Studio, en sélectionnant Targeted Runtimes et en veillant bien à vérifier le temps d'exécution de JBoss EAP 6.
    • Quand vous générez avec Maven, vous pouvez procéder en ajoutant la configuration de dépendance suivante au fichier pom.xml de votre projet.
      <dependency>
         <groupId>org.jboss.logging</groupId>
         <artifactId>jboss-logging</artifactId>
         <version>3.1.2.GA-redhat-1</version>
         <scope>provided</scope>
      </dependency>
    Vous n'avez pas besoin d'inclure les JAR de l'application générée car JBoss EAP 6 les fournit aux applications qui sont déployées.
Une fois que votre projet sera correctement installé, vous devrez suivre les étapes suivantes pour chaque classe à laquelle vous souhaitez ajouter une journalisation.
  1. Ajouter Imports

    Ajouter les déclarations d'importation des espace-noms de classe JBoss Logging que vous allez utiliser. Vous devrez importer au minimum import org.jboss.logging.Logger.
    import org.jboss.logging.Logger;
  2. Créer un objet Logger

    Créer une instance de org.jboss.logging.Logger et l'initialiser en utilisant la méthode statique Logger.getLogger(Class). Red Hat vous recommande de la créer en tant que variable à instance unique pour chaque classe.
    private static final Logger LOGGER = Logger.getLogger(HelloWorld.class);
  3. Ajouter les messages de journalisation

    Ajouter des appels aux méthodes de l'objet Logger à votre code, là où vous souhaitez qu'il envoie des messages. L'objet Logger comprend un certain nombre de méthodes contenant des paramètres différents suivant les types de messages. Les plus faciles à utiliser sont les suivants :
    debog(message objet)
    info(message objet)
    erreur(message objet)
    trace(message objet)
    fatal(message objet)
    Ces méthodes envoient un message de journalisation avec un niveau de journalisation correspondant et le paramètre message comme string.
    LOGGER.error("Configuration file not found.");
    Pour obtenir une liste complète des méthodes JBoss Logging, consulter le package org.jboss.logging dans JBoss EAP 6 API Documentation.

Exemple 5.1. Utilisation de JBoss Logging quand on ouvre un fichier de propriétés

Cet exemple vous montre un exemple de code de classe qui charge une configuration personnalisée pour une application à partir d'un fichier de propriétés. Si le fichier spécifié n'est pas trouvé, un message de journalisation sera enregistré au niveau ERROR.
import org.jboss.logging.Logger;
public class LocalSystemConfig
{
   private static final Logger LOGGER = Logger.getLogger(LocalSystemConfig.class);

   public Properties openCustomProperties(String configname) throws CustomConfigFileNotFoundException
   {
      Properties props = new Properties();
      try 
      {
         LOGGER.info("Loading custom configuration from "+configname);
         props.load(new FileInputStream(configname));
      }
      catch(IOException e) //catch exception in case properties file does not exist
      {
         LOGGER.error("Custom configuration file ("+configname+") not found. Using defaults.");
         throw new CustomConfigFileNotFoundException(configname);
      }
      
      return props;
   }

5.3. La journalisation par déploiement

5.3.1. La journalisation par déploiement

La journalisation par déploiement permet à un développeur de configurer à l'avance la configuration de journalisation de ses applications. Lorsque l'application est déployée, la journalisation commence selon la configuration définie. Les fichiers journaux créés par le biais de cette configuration contiennent uniquement des informations sur le comportement de l'application.
Cette approche a des avantages et des inconvénients pour l'utilisation de la journalisation dans l'ensemble du système. Un des avantages est que l'administrateur de l'instance JBoss EAP n'a pas besoin de configurer la journalisation. Un inconvénient est que la configuration de journalisation par déploiement est en lecture seule au démarrage et ne peut donc pas être modifiée pendant l'exécution.

5.3.2. La journalisation par déploiement vers une application

Pour configurer la journalisation par dépliement, ajouter le fichier de configuration de journalisation logging.properties au déploiement. Ce fichier de configuration est conseillé car il peut être utilisé avec n'importe quelle façade de journalisation dans la mesure où JBoss Log Manager est le gestionnaire de journalisation sous-jacent utilisé.
Si vous utilisez Simple Logging Facade for Java (SLF4J) ou Apache log4j, le fichier de configuration logging.properties est approprié. Si vous utilisez appenders de Apache log4j, alors la configuration fichier log4j.properties sera requise. Le fichier de configuration jboss-logging.properties est pris en charge uniquement pour les déploiements existants hérités.

Procédure 5.1. Ajouter le fichier de configuration à l'application

  • Le répertoire auquel le fichier de configuration est ajouté dépend de la méthode de déploiement : EAR, WAR ou JAR.

    • Déploiement EAR

      Copier le fichier de configuration de journalisation dans le répertoire META-INF.
    • Déploiement WAR ou JAR

      Copier le fichier de configuration de journalisation dans le répertoire META-INF ou WEB-INF/classes.

5.3.3. Exemple de fichier logging.properties

# Additional loggers to configure (the root logger is always configured)
loggers=
# Root logger configuration
logger.level=INFO
logger.handlers=FILE
 
# A handler configuration
handler.FILE=org.jboss.logmanager.handlers.FileHandler
handler.FILE.level=ALL
handler.FILE.formatter=PATTERN
handler.FILE.properties=append,autoFlush,enabled,suffix,fileName
handler.FILE.constructorProperties=fileName,append
handler.FILE.append=true
handler.FILE.autoFlush=true
handler.FILE.enabled=true
handler.FILE.fileName=${jboss.server.log.dir}/app.log
 
# The formatter to use
formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
formatter.PATTERN.properties=pattern
formatter.PATTERN.constructorProperties=pattern
formatter.PATTERN.pattern=%d %-5p %c: %m%n

5.4. Profils de journalisation

5.4.1. Profils de journalisation

Important

Les profils de journalisation ne sont disponibles que dans les versions 6.1.0 ou versions supérieures. Ils ne peuvent pas être configurés par la console de gestion.
Les profils de journalisation sont des ensembles de configuration de journalisation indépendants qui peuvent être assignés à des applications déployées. Un profil de journalisation peut définir les handlers, les catégories et un root logger à la manière du sous-système de journalisation standard, mais ne peut pas recommander la configuration à d'autres profils ou au sous-système de journalisation principal. La conception des profils de journalisation émule le sous-système de journalisation au niveau de l'aisance de configuration.
Les profils de journalisation permettent aux administrateurs de créer des configurations de journalisation spécifiques à une ou à plusieurs applications sans affecter une autre configuration de journalisation. Comme chaque profil est défini dans une configuration serveur, cela implique que la configuration de journalisation peut être modifiée sans exiger que les applications affectées ne soient redéployées.
Chaque profil de journalisation peut avoir la configuration suivante :
  • Un nom unique. Ceci est requis.
  • N'importe quel nombre de log handlers.
  • N'importe quelle catégorie log.
  • Un root logger maximum.
Une application peut spécifier un profil de journalisation à utiliser dans son fichier MANIFEST.MF, en utilisant l'attribut logging-profile.

5.4.2. Spécifier un profil de journalisation dans une application

Une application spécifie le profil de journalisation à utiliser dans son fichier MANIFEST.MF.

Conditions préalables :

  1. Vous devez connaître le nom du profil de journalisation qui a été défini sur le serveur pour cette application. Demandez à votre administrateur de systèmes le nom du profil à utiliser.

Procédure 5.2. Ajouter une configuration de profil de journalisation à une application

  • Modifier MANIFEST.MF

    Si votre application ne possède pas de fichier MANIFEST.MF : le créer avec le contenu suivant, en remplaçant NAME par le nom de profil qui convient.
    Manifest-Version: 1.0
    Logging-Profile: NAME
    Si votre application contient déjà un fichier MANIFEST.MF : ajouter la ligne suivante, en remplaçant NAME par le nom du profil qui convient.
    Logging-Profile: NAME

Note

Si vous utilisez Maven et maven-war-plugin, vous pourrez mettre votre fichier MANIFEST.MF dans src/main/resources/META-INF/ et ajouter la configuration suivante à votre fichier pom.xml.
<plugin>
  <artifactId>maven-war-plugin</artifactId>
  <configuration>
    <archive>
      <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>  
    </archive>
  </configuration>
</plugin>
Quand l'application sera déployée, elle utilisera la configuration qui se trouve dans le profil de journalisation spécifié pour ses messages de journalisation.

Chapitre 6. Internationalisation et localisation

6.1. Introduction

6.1.1. Internationalisation

Le mot internationalisation fait référence au processus de design des logiciels, pour qu'ils puissent être adaptés aux différentes langues et régions sans changements d'ingénierie.

6.1.2. Localisation

Le mot localisation fait référence au processus d'adaptation des logiciels internationaux à une région spécifique ou à une langue spécifique, impliquant le rajout de composants particuliers à la locale et à la traduction de textes.

6.2. Outils JBoss Logging

6.2.1. Aperçu

6.2.1.1. JBoss Logging Tools - Internationalisation et Localisation

JBoss Logging Tools est une API Java qui apporte un soutien pour l'internationalisation et la localisation des messages de journalisation, des messages d'exception et chaînes génériques. En plus de fournir un mécanisme de traduction, les outils JBoss Logging fournissent également un support d'identificateur unique pour chaque message de journalisation.
Les messages et les exceptions internationalisés sont créés en tant que définitions de méthode à l'intérieur des interfaces annotées à l'aide des annotations org.jboss.logging. Il n'est pas nécessaire d'implémenter les interfaces, JBoss Logging Tools le fait au moment de la compilation. Une fois définies, vous pouvez utiliser ces méthodes pour enregistrer des messages ou obtenir des objets d'exception dans votre code.
Les interfaces d'exception et de journalisation internationalisées créées par JBoss Logging Tools peuvent être localisées en créant un fichier de propriétés pour chaque lot contenant les traductions pour un langage et une région spécifiques. JBoss Logging Tools peut générer des fichiers de propriétés de modèle pour chaque lot qui peut ensuite être édité par un traducteur.
JBoss Logging Tools crée une implémentation de chaque lot pour chaque fichier de propriétés de traductions correspondantes dans votre projet. Tous que vous devez faire est d'utiliser les méthodes définies dans les lots et JBoss Logging Tools veillera à ce que l'implémentation qui convient soit invoquée pour vos paramètres régionaux en cours.
Les ids de messages et des codes de projets sont des identificateurs uniques qui sont ajoutés à chaque message du journal. Ces identificateurs uniques peuvent servir de documentation pour faciliter la recherche d'informations sur les messages de journalisation. Avec une documentation suffisante, la signification d'un message de journalisation peut être déterminée par les identificateurs, quelle que soit la langue dans laquelle le message a été rédigé.

6.2.1.2. JBoss Logging Tools Quickstart

JBoss Logging Tools Quickstart, logging-tools, contient un simple projet Maven qui démontre les fonctionnalités de JBoss Logging Tools. A été utilisé extensivement dans cette documentation pour les exemples de code.
Voir ce Quickstart pour obtenir une démonstration complète de toutes les fonctionnalités décrites dans cette documentation.

6.2.1.3. Message Logger

Un Message Logger est une interface utilisée pour définir les messages de journalisation internationalisés. Une interface de Message Logger est annotée par @org.jboss.logging.MessageLogger.

6.2.1.4. Lot de messages

Un lot de messages est une interface qui peut être utilisée pour définir des messages génériques à traduire et des objets d'Exception avec des messages internationalisés. Un lot de messages n'est pas utilisé pour créer des messages de journalisation.
Une interface de lot de messages est annotée par @org.jboss.logging.MessageBundle.

6.2.1.5. Messages de journalisation internationalisés

Les messages de journalisation internationalisés sont des messages de journalisation créés en définissant une méthode dans un Message Logger. La méthode doit contenir les annotations @LogMessage et @Message et spécifier le message de journalisation en utilisant l'attribut de la valeur de @Message. Les messages de journalisation internationalisés sont localisés en fournissant des traductions dans un fichier de propriétés.
JBoss Logging Tools génère les classes de journalisation requises pour chaque traduction au moment de la compilation et invoque les méthodes qui conviennent à la locale en cours d'exécution.

6.2.1.6. Exceptions internationalisées

Une exception internationalisée est un objet d'exception retourné par une méthode définie dans un lot de messages. Les méthodes de lots de messages qui retournent des objets d'Exception Java peuvent être annotés pour définir un message d'exception par défaut. Le message par défaut est remplacé par une traduction, si elle existe dans dans un fichier de propriétés correspondant pour le paramètres régionaux en cours. Les exceptions internationalisées peuvent aussi avoir des codes de projet et des ID de messages assignés.

6.2.1.7. Messages internationalisés

Un message internationalisé est un string retourné par une méthode définie dans un lot de messages. Les méthodes de lots de messages qui retournent des objets String Java peuvent être annotées afin de définir le contenu par défaut de ce string, connu comme message. Le message par défaut est remplacé par une traduction, si une traduction se trouve dans un fichier de propriétés correspondant, pour les paramètres régionaux en cours.

6.2.1.8. Fichiers de propriétés de traduction

Les fichiers de propriétés de traductions sont des fichiers de propriétés Java qui contiennent les traductions des messages à partir d'une interface pour une variante, locale et pays. Les fichiers de propriétés de traductions sont utilisés par JBoss Logging Tools pour créer des classes qui renvoient les messages.

6.2.1.9. Codes de projets de JBoss Logging Tools

Les codes de projets sont des chaînes de caractères qui identifient des groupes de messages. Ils se trouvent au début de chaque message de journalisation, et contiennent l'id du message. Les codes de projets sont définis dans l'attribut projectCode de l'annotation @MessageLogger.

6.2.1.10. Ids de messages de JBoss Logging Tools

Les Ids de messages sont des nombres, qui lorsque combinés à un code de projet, identifient de façon unique un message de journalisation. Les Ids de messages sont affichées au début de chaque message du journal, ajoutés au code de projet du message. Les Ids de messages sont définis par l'attribut id de l'annotation @Message.

6.2.2. Création de loggers, de messages ou d'exceptions internationalisés

6.2.2.1. Créer des messages log internationalisés

Cette tâche vous montre comment utiliser JBoss Logging Tools pour créer des messages de journalisation internationalisés en créant des interfaces MessageLogger. Elle ne traite pas de toutes les caractéristiques optionnelles ou de la localisation des messages de journalisation.
Voir le guide de démarrage logging-tools pour trouver un exemple complet.

Prérequis :

  1. Vous devez déjà posséder un projet Maven en cours. Voir Section 6.2.6.1, « Configuration Maven JBoss Logging Tools ».
  2. Le projet doit avoir la configuration Maven qui convient pour JBoss Logging Tools.

Procédure 6.1. Créer un lot de messages log internationalisés

  1. Créer une interface de Message Logger

    Ajouter une interface Java à votre projet pour contenir les définitions de messages log. Définir l'interface de façon descriptive pour les messages log qui seront définis à l'intérieur.
    L'interface de messages log ont les prérequis suivants :
    • Doit être annotée par @org.jboss.logging.MessageLogger.
    • Doit étendre org.jboss.logging.BasicLogger.
    • L'interface doit définir un champ qui est un Logger typed quiimpléemnte cette interface. Procédez par la méthode getMessageLogger() de org.jboss.logging.Logger.
    package com.company.accounts.loggers;
    
    import org.jboss.logging.BasicLogger;
    import org.jboss.logging.Logger;
    import org.jboss.logging.MessageLogger;
    
    @MessageLogger(projectCode="")
    interface AccountsLogger extends BasicLogger
    {
       AccountsLogger LOGGER = Logger.getMessageLogger(
             AccountsLogger.class,
             AccountsLogger.class.getPackage().getName() );
    }
  2. Ajouter les définitions de méthode

    Ajouter une définition de méthode à l'interface de chaque message log. Nommez chaque méthode descriptivement par rapport au message log qu'elle représente.
    Chaque méthode a les prérequis suivants :
    • La méthode doit renvoyer void.
    • La méthode doit être annotée par @org.jboss.logging.LogMessage.
    • La méthode doit être annotée par @org.jboss.logging.Message.
    • L'attribut de @org.jboss.logging.Message contient le message log par défaut. Il s'agit du message qui est utilisé s'il n'y a pas de traduction.
    @LogMessage
    @Message(value = "Customer query failed, Database not available.")
    void customerQueryFailDBClosed();
    Le niveau de journalisation par défaut est INFO.
  3. Invoquer les méthodes

    Ajouter les appels aux méthodes d'interface dans votre code là où les messages doivent être journalisés. Il n'est pas utile de créer des implémentations des interfaces, le processeur d'annotations le fait pour vous quand le projet est compilé.
    AccountsLogger.LOGGER.customerQueryFailDBClosed();
    Les loggers personnalisés sont des sous-classes de BasicLogger, donc les méthodes de journalisation de BasicLogger (debug(), error() etc) peuvent également être utilisées. Il n'est pas utile de créer d'autres loggers pour enregistrer les messages non internationalisés.
    AccountsLogger.LOGGER.error("Invalid query syntax.");
RÉSULTAT: le projet supporte maintenant un ou plusieurs loggers internationalisés qui peuvent maintenant être localisés.

6.2.2.2. Créer et utiliser des messages internationalisés

Cette tâche vous montre comment créer des messages internationalisés et comment les utiliser. Cette tâche ne couvre pas toutes les fonctionnalités en option ou la localisation de ces messages de journalisation.
Voir le guide de démarrage logging-tools pour trouver un exemple complet.

Pré-requis

  1. La configuration Maven requise pour JBoss Logging Tools a été ajoutée. Voir Section 6.2.6.1, « Configuration Maven JBoss Logging Tools ».

Procédure 6.2. Créer et utiliser des messages internationalisés

  1. Créer une interface pour les exceptions.

    JBoss Logging Tools définit les messages internationalisés dans les interfaces. Nommer chaque interface de manière descriptive pour les messages qui seront définis à l'intérieur.
    L'interface possède les prérequis suivants :
    • Elle doit être déclarée publique
    • Elle doit être annotée par @org.jboss.logging.MessageBundle.
    • L'interface doit définir un champ qui corresponde à un lot de messages du même type que l'interface.
    @MessageBundle(projectCode="")
    public interface GreetingMessageBundle 
    {
       GreetingMessageBundle MESSAGES = Messages.getBundle(GreetingMessageBundle.class);
    }
  2. Ajouter les définitions de méthode

    Ajouter une définition de méthode dans l'interface de chaque message.
    Chaque méthode a les prérequis suivants :
    • Elle doit renvoyer un objet de type String.
    • La méthode doit être annotée par @org.jboss.logging.Message.
    • L'attribut@org.jboss.logging.Message doit être défini à la valeur par défaut du message. Il s'agit du message qui est utilisé si aucune traduction n'est disponible.
    @Message(value = "Hello world.")
       String helloworldString();
  3. Méthodes d'invocation

    Invoquer les méthodes d'interface dans votre application quand vous aurez besoin d'obtenir le message.
    System.console.out.println(helloworldString());
RÉSULTAT : le projet prend maintenant en charge des chaînes de messages internationalisés qui peuvent être localisés.

6.2.2.3. Créer des exceptions personnalisées

Cette tâche vous montre comment créer des exceptions personnalisées et comment les utiliser. Cette tâche ne couvre pas toutes les fonctionnalités en option ou le processus de localisation de ces exceptions.
Voir le guide de démarrage logging-tools pour trouver un exemple complet.
Pour cette tâche, on assume que vous possédez déjà un projet informatique créé dans le Red Hat JBoss Developer Studio ou Maven, et sur lequel vous souhaitez ajouter des exceptions internationalisées.

Procédure 6.3. Créer et utiliser des exceptions internationalisées

  1. Ajouter une configuration JBoss Logging Tools

    Ajouter la configuration de projet qui convient pour prendre en charge JBoss Logging Tools. Voir Section 6.2.6.1, « Configuration Maven JBoss Logging Tools »
  2. Créer une interface pour les exceptions.

    JBoss Logging Tools définit les exceptions internationalisées dans les interfaces. Nommer chaque interface de façon descriptive pour les exceptions qui seront décrites à l'intérieur.
    L'interface possède les prérequis suivants :
    • Elle doit être déclarée public.
    • Elle doit être annotée par @org.jboss.logging.MessageBundle.
    • L'interface doit définir un champ qui corresponde à un lot de messages du même type que l'interface.
    @MessageBundle(projectCode="")
    public interface ExceptionBundle 
    {
       ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class);
    }
    
  3. Ajouter les définitions de méthode

    Ajouter une définition de méthode à l'interface pour chaque exception. Nommez chaque méthode de façon descriptive pour chaque exception qu'elle représente.
    Chaque méthode a les prérequis suivants :
    • Elle doit renvoyer un objet du type Exception ou bien un sous-type d' Exception.
    • La méthode doit être annotée par @org.jboss.logging.Message.
    • L'attribut de @org.jboss.logging.Message doit être défini à la valeur du message d'exception par défaut. Il s'agit du message qui est utilisé s'il n'y a pas de traduction.
    • Si l'exception est retournée à un constructeur qui nécessite des paramètres en plus d'une chaîne de message, alors ces paramètres doivent être fournis dans la définition de méthode à l'aide de l'annotation @param. Les paramètres doivent être du même type et du même ordre que le constructeur.
    @Message(value = "The config file could not be opened.")
    IOException configFileAccessError();
    
    @Message(id = 13230, value = "Date string '%s' was invalid.")
    ParseException dateWasInvalid(String dateString, @Param int errorOffset);
  4. Méthodes d'invocation

    Invoquer les méthodes d'interface de votre code quand vous devrez obtenir une des exceptions. Les méthodes ne lancent pas d'exceptions, elles envoient l'objet d'exception que vous pourrez alors envoyer vous-même.
    try 
    {
       propsInFile=new File(configname);
       props.load(new FileInputStream(propsInFile));
    }
    catch(IOException ioex) //in case props file does not exist
    {
       throw ExceptionBundle.EXCEPTIONS.configFileAccessError(); 
    }
RÉSULTAT : le projet prend maintenant en charge les exceptions internationalisées qui peuvent être localisées.

6.2.3. Localisation de loggers, messages ou exceptions internationalisés

6.2.3.1. Générer des nouveaux fichiers de propriétés de traduction avec Maven

Des projets qui sont construits avec Maven peuvent générer des fichiers de propriétés de traduction vides pour chaque Message Logger et Lots de messages qu'ils contiennent. Ensuite, ces fichiers peuvent être utilisés comme nouveaux fichiers de propriétés de traduction.
La procédure suivante montre comment configurer un projet Maven pour créer des nouveaux fichiers de propriétés de traduction.
Voir le guide de démarrage logging-tools pour trouver un exemple complet.

Conditions préalables :

  1. Vous devez déjà posséder un projet Maven en cours.
  2. Le projet devra avoir été configuré pour JBoss Logging Tools.
  3. Le projet doit contenir une ou plusieurs interfaces qui définissent des exceptions ou des messages de journalisation internationalisés

Procédure 6.4. Générer des nouveaux fichiers de propriétés de traduction avec Maven

  1. Ajouter la configuration Maven

    Ajouter l'argument de compilateur -AgenereatedTranslationFilePath à la configuration du plug-in du compilateur Maven et lui assigner le chemin d'accès où les nouveaux fichiers devront être créés.
    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>2.3.2</version>
       <configuration> 
          <source>1.6</source>
          <target>1.6</target>             
          <compilerArgument>
          -AgeneratedTranslationFilesPath=${project.basedir}/target/generated-translation-files
          </compilerArgument>
          <showDeprecation>true</showDeprecation>
       </configuration>
    </plugin>
    La configuration ci-dessus va créer des nouveaux fichiers dans le répertoire target/generated-translation-files de votre projet Maven.
  2. Créer votre projet

    Créer votre projet en utilisant Maven.
    [Localhost]$ mvn compile
Un fichier de propriétés est créé par interface annotée par @MessageBundle ou @MessageLogger. Les nouveaux fichiers sont créés dans un sous-répertoire qui correspond au package Java dans lequel chaque interface est déclarée.
Chaque nouveau fichier est nommé par la syntaxe suivante, avec InterfaceName comme nom d'interface pour laquelle ce fichier a été créé : InterfaceName.i18n_locale_COUNTRY_VARIANT.properties.
Ces fichiers peuvent alors être copiés dans votre projet pour servir de base à de nouvelles traductions.

6.2.3.2.  Traduire un logger, une exception ou un message internationalisé

Les messages d'exception ou de journalisation définis dans les interfaces qui utilisent JBoss Logging Tools peuvent avoir des traductions dans les fichiers de propriétés.
La procédure suivante vous montre comment créer et utiliser un fichier de propriétés de traduction. On assume que vous possédez déjà un projet avec une ou plusieurs interfaces définies pour les exceptions internationalisées ou pour les messages de journalisation.
Voir le guide de démarrage logging-tools pour trouver un exemple complet.

Pré-requis

  1. Vous devez déjà posséder un projet Maven en cours.
  2. Le projet devra avoir été configuré pour JBoss Logging Tools.
  3. Le projet devra contenir une ou plusieurs interfaces qui définissent les exceptions ou message log d'internationalisation.
  4. Le projet devra être configuré pour générer des fichiers de propriétés modèles.

Procédure 6.5. Traduire un message, une exception ou un message internationalisé

  1. Créer des fichiers de propriétés modèles

    Exécuter la commande mvn compile pour créer les modèles de fichiers de propriétés de traduction.
  2. Ajouter le fichier modèle à votre projet.

    Copier le modèle des interfaces que vous souhaitez traduire depuis le répertoire où elles ont été créées dans le répertoire src/main/resources de votre projet. Les fichiers de propriétés doivent figurer dans le même package des interfaces qu'ils traduisent.
  3. Renommer le fichier modèle copié

    Renommer la copie du fichier modèle selon la traduction qu'il contient. Par exemple, GreeterLogger.i18n_fr_FR.properties.
  4. Traduire le contenu du modèle

    Modifier le nouveau fichier de propriétés de traduction de manière à contenir la traduction appropriée.
    # Level: Logger.Level.INFO
    # Message: Hello message sent.
    logHelloMessageSent=Bonjour message envoyé.
    Répéter les étapes deux, trois et quatre pour chaque traduction de chaque lot exécuté.
RÉSULTAT : le projet contient désormais des traductions pour un ou plusieurs lots de messages ou de loggers. La construction du projet génèrera les classes appropriées aux messages de journalisation avec les traductions fournies. Il n'est pas nécessaire d'invoquer des méthodes explicitement ou de fournir des paramètres pour des langues spécifiques, JBoss Logging Tools choisit automatiquement la bonne classe pour la locale actuelle du serveur d'application.
Le code source des classes générées peuvent être consultées dans target/generated-sources/annotations/.

6.2.4. Personnalisation des messages de journalisation internationalisés

6.2.4.1. Ajouter les ids de messages et les codes de projets aux messages de journalisation

Cette tâche montre comment ajouter les ids de messages et les codes de projets aux messages de journalisation internationalisés créés en utilisant JBoss Logging Tools. Un message de journalisation doit contenir à la fois un code de projet et un id de message afin qu'ils puissent s'afficher dans le journal. Si un message ne contient pas à la fois un code de projet et un id de message, alors aucun ne s'affichera.
Voir le guide de démarrage logging-tools pour trouver un exemple complet.

Conditions préalables

  1. Vous devez déjà posséder un projet avec des messages de journalisation internationalisés. Voir Section 6.2.2.1, « Créer des messages log internationalisés ».
  2. Vous devrez connaître le code de projet que vous utilisez. Vous pouvez utiliser un code de projet simple, ou en définir un différent pour chaque interface.

Procédure 6.6. Ajouter les ids et les codes de projets aux messages de journalisation

  1. Indiquer le code de projet de l'interface.

    Indiquer le code du projet par l'attribut projectCode de l'annotation @MessageLogger qui est attachée à une interface de logger personnalisée. Tous les messages définis dans l'interface utiliseront ce code de projet.
    @MessageLogger(projectCode="ACCNTS")
    interface AccountsLogger extends BasicLogger
    {
    
    }
  2. Indiquer les ids de messages

    Indiquer un id de message pour chaque message qui utilise un attribut id de l'annotation @Message liée à la méthode qui définit le message.
    @LogMessage
    @Message(id=43, value = "Customer query failed, Database not available.")  void customerQueryFailDBClosed();
Les messages de journalisation qui ont à la fois un id de message et un code de projet associés ajouteront ces informations au message enregistré.
10:55:50,638 INFO  [com.company.accounts.ejb] (MSC service thread 1-4) ACCNTS000043: Customer query failed, Database not available.

6.2.4.2. Indiquer le niveau de journalisation d'un message

Le niveau de journalisation par défaut d'un message défini par une interface par JBoss Logging Tools est INFO. Un niveau de journalisation différent peut être indiqué avec l'attribut level de l'annotation @LogMessage jointe à la méthode de journalisation.

Procédure 6.7. Indiquer le niveau de journalisation d'un message

  1. Indiquer l'attribut du niveau

    Ajouter l'attribut level à l'annotation de @LogMessage de la définition de la méthode de message de journalisation.
  2. Allouer un niveau de journalisation

    Allouer l'attribut level à la valeur du niveau de journalisation de ce message. Les valeurs valides de level sont les six constantes énumérées définies dans org.jboss.logging.Logger.Level : DEBUG, ERROR, FATAL, INFO, TRACE, et WARN.
    Import org.jboss.logging.Logger.Level;
    
    @LogMessage(level=Level.ERROR)
    @Message(value = "Customer query failed, Database not available.")
    void customerQueryFailDBClosed();
    
Invoquer une méthode de journalisation dans l'échantillon ci-dessus produira un message de journalisation du niveau ERROR.
10:55:50,638 ERROR  [com.company.app.Main] (MSC service thread 1-4) 
 Customer query failed, Database not available.

6.2.4.3. Personnaliser les messages de journalisation par des paramètres

Les méthodes d'enregistrement personnalisé peuvent définir les paramètres. Ces paramètres sont utilisés pour passer des informations supplémentaires à afficher dans le message journalisé. L'endroit où les paramètres apparaissent dans le message de journal est spécifié dans le message lui-même à l'aide d'une indexation explicite ou ordinaire.

Procédure 6.8. Personnaliser les messages de journalisation avec des paramètres

  1. Ajouter des paramètres à la définition de la méthode

    Les paramètres d'un type donné peuvent être ajoutés à la définition de la méthode. Quel que soit le type, la représentation String du paramètre est ce qui est affiché dans le message.
  2. Ajouter les références de paramètre dans le message de journalisation

    Les références peuvent utiliser des indexations explicites ou ordinaires.
    • Pour utiliser des indexations ordinaires, insérer les caractères %s dans la chaîne de message où vous souhaitez voir apparaître chaque paramètre. Le premier %s insèrera le premier paramètre, le deuxième insèrera le deuxième paramètre, et ainsi de suite.
    • Pour les indexations explicites, insérer les caractères %{#} dans le message où # correspond au numéro du paramètre que vous souhaitez apercevoir.

Important

L'utilisation des indexations explicites permet aux références de paramètres du message d'être dans un ordre différent que celui défini dans la méthode. C'est important pour les messages traduits qui peuvent nécessiter un ordonnancement des paramètres différent.
Le nombre de paramètres doit correspondre au nombre de références aux paramètres dans le message spécifié ou le code ne compilera pas. Un paramètre marqué avec l'annotation @Cause n'est pas inclus dans le nombre de paramètres.

Exemple 6.1. Paramètres de messages qui utilisent des indexations ordinaires.

@LogMessage(level=Logger.Level.DEBUG)
@Message(id=2, value="Customer query failed, customerid:%s, user:%s")
void customerLookupFailed(Long customerid, String username);

Exemple 6.2. Paramètres de messages qui utilisent des indexations explicites

@LogMessage(level=Logger.Level.DEBUG)
@Message(id=2, value="Customer query failed, customerid:%{1}, user:%{2}")
void customerLookupFailed(Long customerid, String username);

6.2.4.4. Indiquer une exception comme cause d'un message de journalisation

JBoss Logging Tools permet à un paramètre d'une méthode de journalisation personnalisée d'être défini comme cause du message. Ce paramètre doit être du type Throwable ou d'une de ses sous-classes et doit être marqué par l'annotation @Cause . Ce paramètre ne peut pas être référencé dans le message de journal comme les autres paramètres et il s'affiche à la suite du message de journalisation.
La procédure suivante montre comment mettre à jour une méthode de journalisation en utilisant le paramètre @Cause pour indiquer l'exception "qui cause". On suppose que vous avez déjà créé des messages de journalisation internationalisés auxquels vous souhaitez ajouter cette fonctionnalité.

Procédure 6.9. Indiquer une exception comme cause d'un message de journalisation

  1. Ajouter le paramètre

    Ajouter un paramètre Throwable ou une sous-classe à la méthode.
    @LogMessage
    @Message(id=404, value="Loading configuration failed. Config file:%s")
    void loadConfigFailed(Exception ex, File file);
  2. Ajouter l'annotation

    Ajouter l'annotation @Cause au paramètre.
    import org.jboss.logging.Cause
    
    @LogMessage
    @Message(value = "Loading configuration failed. Config file: %s")
    void loadConfigFailed(@Cause Exception ex, File file);
    
  3. Invoquer la méthode

    Quand la méthode est invoquée dans votre code, un objet du type qui convient devra être passé et sera affiché à la suite du message de journalisation.
    try 
    {
       confFile=new File(filename);
       props.load(new FileInputStream(confFile));
    }
    catch(Exception ex) //in case properties file cannot be read
    {
         ConfigLogger.LOGGER.loadConfigFailed(ex, filename);
    }
    
    Vous trouverez ci-dessous la sortie des extraits de code ci-dessus, si le code a lancé une exception de type FileNotFoundException.
    10:50:14,675 INFO [com.company.app.Main] (MSC service thread 1-3) Loading configuration failed. Config file: customised.properties
    java.io.FileNotFoundException: customised.properties (No such file or directory)
       at java.io.FileInputStream.open(Native Method)
       at java.io.FileInputStream.<init>(FileInputStream.java:120)
       at com.company.app.demo.Main.openCustomProperties(Main.java:70)
       at com.company.app.Main.go(Main.java:53)
       at com.company.app.Main.main(Main.java:43)

6.2.5. Personnalisation des exceptions internationalisées

6.2.5.1. Ajouter les ids et les codes de projets aux messages d'exceptions

La procédure suivante montre les étapes requises pour ajouter des ID de message et des codes de projets aux messages d'exception internationalisés créés en utilisant les JBoss Logging Tools.
Les ID de messages et les codes de projet sont des identificateurs uniques qui sont ajoutés à chaque message affiché par les exceptions internationalisées. Ces codes d'identification permettent de créer une référence pour tous les messages d'exception d'une application pour qu'une personne puisse rechercher le sens d'un message d'exception écrit dans une langue qu'elle ne comprent pas.

Conidtions préalables

  1. Vous devrez déjà avoir un projet avec des exceptions internationalisées. Voir Section 6.2.2.3, « Créer des exceptions personnalisées ».
  2. Vous devrez connaître le code de projet que vous utilisez. Vous pouvez utiliser un code de projet simple, ou en définir un différent pour chaque interface.

Procédure 6.10. Ajouter les ids et les codes de projets aux messages d'exceptions

  1. Indiquer un code de projet

    Indiquer le code de projet en utilisant l'attribut projectCode de l'annotation @MessageBundle attachée à une interface de lot d'exceptions. Tous les messages qui sont définis dans l'interface utiliseront ce code de projet.
    @MessageBundle(projectCode="ACCTS")
    interface ExceptionBundle
    {
       ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class);
    }
  2. Indiquer les ID de messages

    Indiquer l'id de message pour chaque exception en utilisant l'attribut id de l'annotation @Message associée à la méthode qui définit l'exception.
    @Message(id=143, value = "The config file could not be opened.")
    IOException configFileAccessError();

Important

Un message qui comprend à la fois un code de projet et un ID de message les affiche à la suite du message. Si un message ne les possède pas, ils ne seront pas affichés.

Exemple 6.3. Créer des exceptions internationalisées

Cette interface de lot d'exceptions possède le code de projet d'ACCTS, avec une simple méthode d'exception ayant comme Id 143.
@MessageBundle(projectCode="ACCTS")
interface ExceptionBundle
{
    ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class);

    @Message(id=143, value = "The config file could not be opened.")
    IOException configFileAccessError();
}
L'objet d'exception peut être obtenu et lancé grâce au code suivant.
throw ExceptionBundle.EXCEPTIONS.configFileAccessError();
Cela aurait pour effet d'afficher un message d'exception qui ressemble à ce qui suit :
Exception in thread "main" java.io.IOException: ACCTS000143: The config file could not be opened.
at com.company.accounts.Main.openCustomProperties(Main.java:78)
at com.company.accounts.Main.go(Main.java:53)
at com.company.accounts.Main.main(Main.java:43)

6.2.5.2. Personnaliser les messages d'exception avec des paramètres

Les méthodes de lots d'exceptions qui définissent des exceptions peuvent définir les paramètres. Ces paramètres sont utilisés pour passer des informations supplémentaires à afficher dans le message d'exception. L'endroit où les paramètres apparaissent dans le message d'exception est spécifié dans le message lui-même à l'aide d'une indexation explicite ou ordinaire.
La procédure suivante vous montre les étapes requises pour utiliser les paramètres de méthodes pour personnaliser des exceptions de méthodes.

Procédure 6.11. Personnaliser les messages d'exception avec les paramètres

  1. Ajouter des paramètres à la définition de la méthode

    Les paramètres d'un type donné peuvent être ajoutés à la définition de la méthode. Quel que soit le type, la représentation String du paramètre est ce qui est affiché dans le message.
  2. Ajouter les références de paramètre au message d'exception

    Les références peuvent utiliser des indexations explicites ou ordinaires.
    • Pour utiliser des indexations ordinaires, insérer les caractères %s dans la chaîne de message où vous souhaitez voir apparaître chaque paramètre. Le premier %s insèrera le premier paramètre, le deuxième insèrera le deuxième paramètre, et ainsi de suite.
    • Pour les indexations explicites, insérer les caractères %{#} dans le message où # correspond au numéro du paramètres que vous souhaitez apercevoir.
    L'utilisation des indexations explicites permet aux références de paramètres du message d'être dans un ordre différent que celui défini dans la méthode. C'est important pour les messages traduits qui peuvent nécessiter un ordonnancement des paramètres différent.

Important

Le nombre de paramètres doit correspondre au nombre de références aux paramètres dans le message spécifié ou le code ne compilera pas. Un paramètre marqué avec l'annotation @Cause n'est pas inclus dans le nombre de paramètres.

Exemple 6.4. Utilisation des indexations ordinaires

@Message(id=143, value = "The config file %s could not be opened.")
IOException configFileAccessError(File config);

Exemple 6.5. Utilisation des indexations explicites

@Message(id=143, value = "The config file %{1} could not be opened.")
IOException configFileAccessError(File config);

6.2.5.3. Indiquer une exception comme cause d'une autre exception

Les exceptions renvoyées par les méthodes de lots d'exceptions peuvent avoir une autre exception spécifiée comme cause sous-jacente. Cela se fait par l'ajout d'un paramètre à la méthode et en annotant le paramètre @Cause. Ce paramètre est utilisé pour passer l'exception de «cause». Ce paramètre ne peut pas être référencé dans le message d'exception.
La procédure suivante montre comment mettre à jour une méthode de lot d'exception en utilisant le paramètre @Cause pour indiquer l'exception de « cause ». On suppose que vous avez déjà créé des messages de journalisation internationalisés auxquels vous souhaitez ajouter cette fonctionnalité.

Procédure 6.12. Indiquer une exception comme « cause » d'une autre exception

  1. Ajouter le paramètre

    Ajouter un paramètre Throwable ou une sous-classe à la méthode.
    @Message(id=328, value = "Error calculating: %s.")
    ArithmeticException calculationError(Throwable cause, String msg);
  2. Ajouter l'annotation

    Ajouter l'annotation @Cause au paramètre.
    import org.jboss.logging.Cause
    
    @Message(id=328, value = "Error calculating: %s.")
    ArithmeticException calculationError(@Cause Throwable cause, String msg);
  3. Invoquer la méthode

    Invoquer la méthode d'interface pour obtenir un objet d'exception. Le cas d'utilisation le plus commun est de lancer une exception à partir d'un catch block en utilisant l'exception récupérée comme « cause ».
    try 
    {
       ...
    }
    catch(Exception ex)
    {
       throw ExceptionBundle.EXCEPTIONS.calculationError(
                                        ex, "calculating payment due per day");
    }

Exemple 6.6. Indiquer une exception comme « cause » d'une autre exception

Ce lot d'exceptions définit une méthode simple qui renvoie une exception du type ArithmeticException.
@MessageBundle(projectCode = "TPS")
interface CalcExceptionBundle 
{
	CalcExceptionBundle EXCEPTIONS = Messages.getBundle(CalcExceptionBundle.class);

    @Message(id=328, value = "Error calculating: %s.")
    ArithmeticException calcError(@Cause Throwable cause, String value);

}
Cet extrait de code exécute une opération qui lève une exception parce qu'il tente de diviser un entier par zéro. L'exception est interceptée et une nouvelle exception est créée en utilisant la première comme « cause ».
int totalDue = 5;
int daysToPay = 0;
int amountPerDay;

try
{
   amountPerDay = totalDue/daysToPay;
}
catch (Exception ex)
{
   throw CalcExceptionBundle.EXCEPTIONS.calcError(ex, "payments per day");
}
Voici à quoi ressemble le message :
Exception in thread "main" java.lang.ArithmeticException: TPS000328: Error calculating: payments per day.
	at com.company.accounts.Main.go(Main.java:58)
	at com.company.accounts.Main.main(Main.java:43)
Caused by: java.lang.ArithmeticException: / by zero
	at com.company.accounts.Main.go(Main.java:54)
	... 1 more

6.2.6. Référence

6.2.6.1. Configuration Maven JBoss Logging Tools

Pour construire un projet Maven qui utilise JBoss Logging Tools pour l'internationalisation, vous devrez appliquer les changements suivants à la configuration du projet dans le fichier pom.xml :
Veuillez vous référer au quick start logging-tools pour obtenir un exemple de fichier pom.xml complet.
  1. Les dépendances Maven de jboss-logging et jboss-logging-processor doivent être ajoutées. Les deux dépendances sont disponibles sur JBoss EAP 6, ainsi l'élément scope de chaque dépendance peut être définie comme provided tel qu'il a été montré.
    <dependency>
       <groupId>org.jboss.logging</groupId>
       <artifactId>jboss-logging-processor</artifactId>
       <version>1.0.0.Final</version>
       <scope>provided</scope>
    </dependency>
    
    <dependency>
       <groupId>org.jboss.logging</groupId>
       <artifactId>jboss-logging</artifactId>
       <version>3.1.0.GA</version>
       <scope>provided</scope>
    </dependency>
  2. Le maven-compiler-plugin doit utiliser au minimum la version 2.2 et être configuré pour la cible et les sources générées de 1.6.
    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>2.3.2</version>
       <configuration>
          <source>1.6</source>
          <target>1.6</target>
       </configuration>
    </plugin>

6.2.6.2. Format de fichier de propriétés de traduction

Les fichiers de propriété utilisés pour les traductions de messages dans JBoss Logging Tools sont des fichiers de propriétés Java standards. Le format du fichier est le format key=value simple, orienté-ligne, décrit dans la documentation pour la classe java.util.Properties, http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html.
Le format du nom du fichier a le format suivant :
InterfaceName.i18n_locale_COUNTRY_VARIANT.properties 
  • InterfaceName est le nom de l'interface où les traductions s'appliquent.
  • locale, COUNTRY, et VARIANT identifient les paramètres régionaux correspondant à la traduction.
  • locale et COUNTRY indiquent la langue et le pays en utilisant respectivement les codes Langues et Pays ISO-639 et ISO-3166. COUNTRY est optionnel.
  • VARIANT est un identifiant optionnel pouvant être utilisé pour identifier les traductions qui ne s'appliquent qu'à un système d'exploitation ou à un navigateur en particulier.
Les propriétés contenues dans le fichier de traduction sont les noms des méthodes de l'interface en cours de traduction. La valeur assignée de la propriété est la traduction. Si une méthode est surchargée, c'est indiqué en ajoutant un point, puis le nombre de paramètres pour le nom. Les méthodes de traduction peuvent seulement être surchargées en fournissant un nombre différent de paramètres.

Exemple 6.7. Échantillon de fichier de propriétés de traductions

Nom du fichier : GreeterService.i18n_fr_FR_POSIX.properties.
# Level: Logger.Level.INFO
# Message: Hello message sent.
logHelloMessageSent=Bonjour message envoyé.

6.2.6.3. Références aux annotations de JBoss Logging Tools

Les annotations suivantes sont définies dans JBoss Logging pour pouvoir être utilisées pour l'internationalisation et la localisation des messages de journalisation, strings et exceptions.

Tableau 6.1. Annotations de JBoss Logging Tools

Annotation Cible Description Attributs
@MessageBundle Interface
Définit l'interface en tant que Lot de Messages
projectCode
@MessageLogger Interface
Définit l'interface en tant que Message Logger
projectCode
@Message Méthode
Peut être utilisé dans les Lots de Messages et Message Loggers. Dans un Message Logger, définit une méthode comme étant un logger localisé. Dans un Lot de Messages, définit la méthode comme étant une méthode qui renvoie une Chaîne localisées ou un objet d'Exception.
value, id
@LogMessage Méthode
Définit une méthode dans un Message Logger comme étant une méthode de logging.
level (default INFO)
@Cause Paramètre
Définit un paramètre comme étant un paramètre faisant passer une exception en tant que cause d'un Message Log ou autre exception.
-
@Param Paramètre
Définit un paramètre comme étant un paramètre passé au constructeur d'une Exception.
-

Chapitre 7. Enterprise JavaBeans

7.1. Introduction

7.1.1. Entreprise JavaBeans

Enterprise JavaBeans (EJB) 3.1 est une API pour développer des applications de Java EE distribuées, transactionnelles, sécurisées et portables grâce à l'utilisation des composants côté serveur appelés Enterprise Beans. Enterprise Beans implémente la logique métier d'une application, de manière découplée, qui encourage la réutilisation. Enterprise JavaBeans 3.1 est documenté dans la spécification Java EE JSR-318.
JBoss EAP 6 prend en charge la génération d'applications qui utilisent la spécification Enterprise JavaBeans 3.1.

7.1.2. Groupe de fonctionnalités EJB 3.1

Les fonctionnalités suivantes sont prises en charge dans EJB 3.1
  • Session Beans
  • Message Driven Beans
  • Vues non-interfaces
  • Interfaces locales
  • Interfaces distantes
  • Services web JAX-WS
  • Services web JAX-RS
  • Service de minuterie
  • Appels asynchrones
  • Intercepteurs
  • Interopérabilité RMI/IIOP
  • Support de transaction
  • Sécurité
  • API intégrable
Les fonctionnalités suivantes sont prises en charge dans EJB 3.1 mais sont susceptibles d'être supprimées. Cela signifie qu'elles seront certainement optionnelles dans Java EE 7.
  • Entity Beans (persistance gérée-bean et conteneur))
  • Vues client Entity Beans EJB 2.1
  • EJB Query Language (EJB QL)
  • Services Web basés JAX-RPC (points de terminaison et vues client)

7.1.3. EJB 3.1 Lite

EJB Lite est un sous-ensemble de la spécification EJB 3.1. Il fournit une version simplifiée de la spécification EJB 3.1 complète dans le cadre du profil web Java EE 6.
EJB Lite simplifie l'implémentation de la logique métier dans les applications web avec les beans entreprise en :
  1. ne supportant que les fonctionnalités qui sont utiles pour les applications-web, et
  2. en permettant aux EJB d'être déployés dans le même fichier WAR sous forme d'application web.

7.1.4. Fonctionnalités EJB 3.1 Lite

EJB Lite inclut les fonctionnalités suivantes :
  • Session Beans sans état, avec état, et singleton
  • Interfaces commerciales locales et beans "sans interface"
  • Intercepteurs
  • Transactions gérées-conteneur et gérées-bean
  • Sécurité déclarative et programmatique
  • API intégrable
Les fonctionnalités suivantes d'EJB 3.1 ne sont par incluses spécifiquement :
  • Interfaces éloignées
  • Intéropérabilité RMI-IIOP
  • Ponts de terminaison de services web JAX-WS
  • Service de minuterie EJB
  • Invocations de session beans asynchrones
  • Message-driven beans

7.1.5. Beans Enterprise

Les beans enterprise sont des composants d'applications côté serveur, définis dans la spécification Enterprise JavaBeans (EJB) 3.1, JSR-318. Les beans enterprise sont conçus pour l'implémentation d'une logique commerciale d'application d'une manière découplée, pour encourager sa réutilisation.
Les beans enterprise sont écrits comme des classes Java et sont annotés avec les annotations EJB appropriées. Ils peuvent être déployés sur le serveur d'applications dans leurs propres archives (un fichier JAR) ou être déployés dans le cadre d'une application Java EE. Le serveur d'applications gère le cycle de vie de chaque bean entreprise et leur fournit des services comme la sécurité, les transactions et la gestion de concurrence.
Un bean enterprise peut également définir un nombre d'interfaces de métier. Les interfaces de métier vous proposent un plus grand contrôle sur les méthodes bean disponibles aux clients et peut également vous donner accès aux clients qui exécutent dans les JVM à distance.
Il existe trois types de beans enterprise : les session beans, les message-driven beans et les entity beans.

Important

Les entity beans sont maintenant obsolètes dans EJB 3.1 et Red Hat recommande d'utiliser des entités JPA à la place. Red Hat ne recommande d'utiliser des entity beans que pour les compatibilités rétroactives avec les systèmes hérités.

7.1.6. Écriture des beans Enterprise

Les beans Enterprise sont des composants côté serveur conçus pour encapsuler la logique métier d'une manière découplée d'une application client spécifique. En implémentant votre logique de métier dans des beans Enterprise, vous serez en mesure de réutiliser ces beans dans plusieurs applications.
Les beans Enterprise sont écrits sous forme de classes Java annotées et n'ont pas besoin d'implémenter des interfaces EJB particulières, ni de représenter des sous-classes de classes EJB Super pour pouvoir être considérés bean Enterprise.
Les Bean Enterprise EJB 3.1 sont empaquetés et déployés dans des fichiers JAR (Archives Java). Un fichier JAR Bean Enterprise peut être déployé dans votre serveur d'applications, ou être inclus dans un fichier EAR et déployé dans cette application. Il est également possible de déployer des beans Enterprise dans un fichier WAR en parallèle à une application web.

7.1.7. Interfaces métier de Session Bean

7.1.7.1. Interfaces de métier Bean Enterprise

Une interface de métier EJB est une interface Java écrite par le développeur de bean qui fournit les déclarations de méthodes publiques d'un bean de session disponible aux clients. Les beans de session peuvent implémenter un certain nombre d'interface ou pas du tout (bean "no-interface").
Les interfaces de métier peuvent être déclarées comme interfaces locales ou à distance, mais pas les deux à la fois.

7.1.7.2. Interfaces de métier locales EJB

Une interface de métier locale EJB déclare les méthodes qui sont disponibles quand le bean et le client sont dans la même JVM. Quand un bean de session implémente une interface de métier locale, seules les méthodes déclarées dans cette interface seront disponibles aux clients.

7.1.7.3. Interfaces métier à distance EJB

Une interface de métier à distance EJB déclare les méthodes qui sont disponibles pour les clients à distance. L'accès à distance à un bean de session qui implémente une interface à distance est fourni automatiquement par un conteneur EJB.
Un client à distance est un client qui exécute sur une JVM différente et peut inclure des applications de bureau ainsi que des applications web, des services et bean Enterprise déployés dans un serveur d'applications différent.
Les clients locaux peuvent accéder aux méthodes exposées par une interface de métier à distance. Cela est effectué par les mêmes méthodes que pour les clients à distance et comprend les mêmes protocoles de départ que pour une demande à distance.

7.1.7.4. Beans EJB no-interface

Un bean de session qui n'implémente pas d'interface de métier s'appelle un bean « no-interface ». Toutes les méthodes publiques de beans no-interface sont accessibles aux clients locaux.
Un bean de session qui implémente une interface de métier peut également être écrit pour exposer un affichage « no-interface ».

7.2. Créer des projets Enterprise Bean

7.2.1. Créer un projet d'archives EJB avec le Red Hat Studio JBoss Developer

Cette tâche décrit comment créer un EJB (Enterprise JavaBeans) avec le Red Hat Studio JBoss Developer

Conditions préalables

  • Un serveur et un temps d'exécution du serveur ont été établis pour JBoss EAP 6.

Procédure 7.1. Créer un projet EJB dans le Red Hat JBoss Developer Studio

  1. Créer un nouveau projet

    Pour ouvrir l'Assistant New EJB Project, naviguer jusqu'au menu Fichier, sélectionner Nouveau puis Projet EJB.
    Assistant New EJB Project

    Figure 7.1. Assistant New EJB Project

  2. Détails

    Fournir les informations suivantes :
    • Nom du projet.
      Il s'agira du nom du projet qui apparaît dans Red Hat JBoss Developer Studio, et également le nom du fichier par défaut du fichier JAR déployé.
    • Emplacement du projet
      Le répertoire où les fichiers du projet seront sauvegardés. La valeur par défaut est un répertoire qui se trouve dans l'espace de travail en cours.
    • Runtime cible.
      Il s'agit du runtime de serveur utilisé pour le projet. Il devra être défini au même runtime JBoss EAP 6, qui est utilisé par le serveur dans lequel vous souhaitez déployer.
    • Version de module EJB. Il s'agit de la version de la spécification EJB avec laquelle vos beans Enterprise devront se conformer. Red Hat recommande que vous utilisiez 3.1.
    • Configuration. Cela vous permet d'ajuster les fonctionnalités prises en charge par votre projet. Utiliser la configuration par défaut pour le runtime que vous avez sélectionné.
    Cliquer sur Suivant pour continuer.
  3. Java Build Configuration

    Cet écran vous permet de personnaliser les répertoires qui contiennent les fichiers source de Java et le répertoire où les résultats de la génération se trouvent.
    Conservez cette configuration sans y apporter aucun changement et cliquer sur le bouton Suivant.
  4. Paramétrage du Module EJB

    Cocher la case Generate ejb-jar.xml deployment descriptor (Générer descripteur de déploiement ejb-jar.xml) si un descripteur de déploiement est requis. Le descripteur de déploiement est une option dans EJB 3.1, et peut être rajouté plus tard, si nécessaire.
    Cliquer sur le bouton Terminé et le projet sera créé. Il s'affichera dans Project Explorer.
    Nouveau projet EJB créé dans Project Explorer

    Figure 7.2. Nouveau projet EJB créé dans Project Explorer

  5. Ajouter l'artifact généré dans le serveur en vue du déploiement

    Ouvrir le dialogue Add and Remove en cliquant à droite sur le serveur dans lequel vous souhaitez générer l'artefact dans l'onglet serveur, et sélectionner 'Ajouter et Supprimer'.
    Sélectionner la ressource à déployer à partir de la colonne Available et cliquer sur le bouton Add. La ressource sera déplacée dans la colonne Configured. Cliquer sur Finish pour fermer le dialogue.
    Ajouter et Supprimer le dialogue

    Figure 7.3. Ajouter et Supprimer le dialogue

Résultat

Vous avez maintenant un Projet EJB dans Red Hat JBoss Developer Studio pouvant être construit et déployé pour un serveur spécifique.

Si aucun bean Enterprise n'est ajouté au projet, alors Red Hat JBoss Developer Studio affichera l'avertissement « un module EJB doit contenir un ou plusieurs beans Enterprise. » Cet avertissement disparaîtra une fois qu'un ou plusieurs beans Enterprise beans auront été ajoutés au projet.

7.2.2. Créer un projet EJB Archive dans Maven.

Cette tâche montre comment créer, dans Maven, un projet contenant un ou plusieurs Beans Enterprise empaquetés dans un fichier JAR.

Prérequis :

  • Maven est déjà installé.
  • Vous comprenez les bases d'utilisation de Maven.

Procédure 7.2. Créer un projet EJB Archive dans Maven

  1. Créer le projet Maven

    Un projet EJB peut être créé en utilisant le système archétype de Maven et l'archétype ejb-javaee6. Pour ce faire, exécuter la commande mvn avec les paramètres suivants :
     mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee6 
    Maven vous demandera le groupId, le artifactId, la version et le package de votre projet.
    [localhost]$ mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee6
    [INFO] Scanning for projects...
    [INFO]                                                                         
    [INFO] ------------------------------------------------------------------------
    [INFO] Building Maven Stub Project (No POM) 1
    [INFO] ------------------------------------------------------------------------
    [INFO] 
    [INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>>
    [INFO] 
    [INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<<
    [INFO] 
    [INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom ---
    [INFO] Generating project in Interactive mode
    [INFO] Archetype [org.codehaus.mojo.archetypes:ejb-javaee6:1.5] found in catalog remote
    Define value for property 'groupId': : com.shinysparkly
    Define value for property 'artifactId': : payment-arrangments
    Define value for property 'version':  1.0-SNAPSHOT: : 
    Define value for property 'package':  com.shinysparkly: : 
    Confirm properties configuration:
    groupId: com.company
    artifactId: payment-arrangments
    version: 1.0-SNAPSHOT
    package: com.company.collections
    Y: : 
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 32.440s
    [INFO] Finished at: Mon Oct 31 10:11:12 EST 2011
    [INFO] Final Memory: 7M/81M
    [INFO] ------------------------------------------------------------------------
    [localhost]$
  2. Ajouter vos beans enterprise

    Ecrire vos beans enterprise et les ajouter au projet sous le répertoire src/main/java dans le sous-répertoire approprié pour le paquetage du bean.
  3. Créer votre projet

    Pour construire ce projet, exécuter la commande mvn package dans le même répertoire que le fichier pom.xml. Cela compilera les classes Java et empaquettera le fichier JAR. Le fichier JAR construit se nomme artifactId-version.jar et se trouve dans le répertoire target/.
RÉSULTAT : Vous avez maintenant un projet Maven qui construit et empaquette un fichier JAR. Ce projet peut contenir des beans enterprise et le fichier JAR peut être déployé vers un serveur d'application.

7.2.3. Créer un projet EAR contenant un projet EJB

Cette tâche décrit comment créer dans Red Hat JBoss Developer Studio un nouveau projet Enterprise Archive (EAR) contenant un projet EJB.

Conditions préalables

Procédure 7.3. Créer un projet EAR contenant un projet EJB

  1. Ouvrir le nouvel Assistant de projet de l'application EAR.

    Naviguer vers le menu File, sélectionner New, puis Project et l'assistant New Projet apparaîtra. Sélectionner Java EE/Enterprise Application Project et cliquer sur le bouton Next.
    Nouvel Assistant de Projet de l'Application EAR.

    Figure 7.4. Nouvel Assistant de Projet de l'Application EAR.

  2. Fournir des Informations

    Fournir les informations suivantes :
    • Nom du projet.
      Il s'agira du nom du projet qui apparaît dans Red Hat JBoss Developer Studio, et également le nom du fichier par défaut du fichier EAR déployé.
    • Emplacement du projet
      Le répertoire où les fichiers du projet seront sauvegardés. La valeur par défaut est un répertoire qui se trouve dans l'espace de travail en cours.
    • Runtime cible.
      Il s'agit du runtime de serveur utilisé pour le projet. Il devra être défini au même runtime JBoss EAP 6 qui est utilisé par le serveur dans lequel vous souhaitez déployer.
    • Version EAR.
      C'est la version des spécifications de Java Enterprise Edition auquelles votre projet devra se conformer. Red Hat recommande d'utiliser la version 6.
    • Configuration. Cela vous permet d'ajuster les fonctionnalités prises en charge par votre projet. Utiliser la configuration par défaut pour le runtime que vous avez sélectionné.
    Cliquer sur Suivant pour continuer.
  3. Ajouter un nouveau module EJB.

    Les nouveaux modules peuvent être ajoutés à partir de la page Enterprise Application de l'assistant. Pour ajouter un nouveau projet EJB en tant que module, veuillez suivre les étapes suivantes :
    1. Ajouter un nouveau module EJB.

      Cliquer sur New Module, décocher la case Create Default Modules, sélectionner Enterprise Java Bean et cliquer sur Next. L'assistant New EJB Project apparaîtra.
    2. Créer un projet EJB.

      L'assistant New EJB Project est le même que l'assistant utilisé pour créer de nouveaux projets EJB autonomes et est décrit dans Section 7.2.1, « Créer un projet d'archives EJB avec le Red Hat Studio JBoss Developer ».
      Les détails minimales requis pour créer le project sont les suivants :
      • Nom du Projet
      • Runtime de la cible
      • Version Module EJB
      • Configuration
      Toutes les autres étapes de l'assistant sont optionnelles. Cliquer sur Finish Terminé) pour finir de créer le projet EJB.
    Le projet EJB tout juste créé est listé dans les dépendances du module Java EE et la case est cochée.
  4. Optionnel : ajouter un descripteur de déploiement d'application.xml

    Cocher la case Generate application.xml deployment descriptor Générer descripteur de déploiement d'application.xmlle cas échéant.
  5. Cliquer sur Finish

    Deux nouveaux projets vont apparaître, le projet EJB et le projet EAR.
  6. Ajouter l'artifact généré dans le serveur en vue du déploiement

    Ouvrir la boîte de dialogue Add and Remove (Ajouter et Supprimer) en cliquant avec le bouton droit de la souris, dans l'onglet Servers (Serveurs), sur le serveur dans lequel vous souhaitez déployer l'artéfact de la build dans l'onglet serveur, et sélectionner Add and Remove.
    Sélectionner la ressource EAR à déployer depuis la colonne Available (disponible) et cliquer sur le bouton Add. La ressource sera déplacée vers la colonne Configured. Cliquer sur Finish pour fermer la boîte de dialogue.
    Ajouter et Supprimer le dialogue

    Figure 7.5. Ajouter et Supprimer le dialogue

Résultat

Vous avez maintenant un Projet d'application enterprise avec un Projet EJB membre. Celui-ci se créera et se déploiera vers le serveur indiqué en tant que déploiement EAR unique contenant un sous-déploiement EJB.

7.2.4. Ajouter un descripteur de déploiement à un projet EJB

On peut ajouter un descripteur de déploiement à un projet EJB qui a été créé sans descripteur. Pour cela, suivre la procédure ci-dessous.

Conditions préalables :

  • Vous avez un projet EJB dans Red Hat JBoss Developer Studio auquel vous souhaitez ajouter un descripteur de déploiement EJB.

Procédure 7.4. Ajouter un descripteur de déploiement à un projet EJB.

  1. Ouvrir le projet

    Ouvrir un projet dans Red Hat JBoss Developer Studio.
  2. Ajouter un descipteur de déploiement

    Cliquer à droite sur le dossier Descripteur de déploiement dans la vue du projet et sélectionner Generate Deployment Descriptor Stub (Générer Descripteur de déploiement).
    Ajouter un descripteur de déploiement

    Figure 7.6. Ajouter un descripteur de déploiement

Le nouveau fichier, ejb-jar.xml, sera créé dans ejbModule/META-INF/. Cliquer deux fois dans le dossier Descripteur de déploiement dans la vue du projet, ce qui permettra d'ouvrir ce fichier également.

7.3. Session Beans

7.3.1. Session Beans

Les session beans sont des beans Enterprise qui encapsulent un ensemble de processus métier connexes ou des tâches, et qui sont injectés dans les classes qui en ont fait la demande. Il existe trois types de session beans : sans état, avec état et singleton.

7.3.2. Stateless Session Beans

Les Stateless Session Beans sont à la fois les types de bean de session les plus simples et les plus largement utilisés. Ils fournissent des méthodes commerciales à des applications client, mais ne maintiennent pas d'état entre les appels de méthode. Chaque méthode est une tâche complète, qui ne s'appuie pas sur un état partagé au sein de ce bean de session. Parce qu'il n'y a aucun état, le serveur d'application n'est pas tenu de s'assurer que chaque appel de méthode soit effectué sur la même instance. Cela rend les beans de session stateless très efficaces et évolutifs.

7.3.3. Stateful Session Beans

Les Stateful Session Beans sont des beans Enterprise qui fournissent des modèles commerciaux aux applications client et qui maintiennent un état conversationnel avec le client. Ils doivent être utilisés pour des tâches en plusieurs étapes - appels de méthode - chacun répondant à l'état de l'étape précédente ayant été maintenue. Le serveur d'applications veille à ce que chaque client reçoive la même instance d'un stateful session bean pour chaque appel de méthode.

7.3.4. Singleton Session Beans

Les Singleton Session Beans sont des beans de session qui sont instanciés une fois par application, et chaque requête de client de Singleton Bean va dans la même instance. Les Singleton Beans représentent une implémentation du Singleton Design Pattern décrit dans l'ouvrage Design Patterns: Elements of Reusable Object-Oriented Software d'Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides; publié par Addison-Wesley en 1994.
Les beans Singleton fournissent la plus petite empreinte parmi tous les types de beans de session, mais ils doivent être thread-safe. EJB 3.1 fournit la CMC (de l'anglais Container Managed Concurrency) pour permettre aux développeurs d'implémenter des beans Singleton facilement. Cependant, les beans Singleton peuvent également être écrits grâce à un code multi-thread BMC (de l'anglais Bean Managed Concurrency) si CMC n'offre pas suffisamment de flexibilité.

7.3.5. Ajouter des Session Beans à un Projet dans Red Hat JBoss Developer Studio

Red Hat JBoss Developer Studio possède plusieurs assistants qui peuvent être utilisés pour créer rapidement des classes bean Enterprise. La procédure suivante vous montre comment utiliser les assistants du Red Hat JBoss Developer Studio pour ajouter un session bean à un projet.

Conditions préalables :

  • Vous avec un EJB ou Dynamic Web Project dans Red Hat JBoss Developer Studio auquel vous souhaitez ajouter un ou plusieurs session beans.

Procédure 7.5. Ajouter des Session Beans à un Projet dans Red Hat JBoss Developer Studio

  1. Ouvrir le projet

    Ouvrir un projet dans Red Hat JBoss Developer Studio.
  2. Ouvrir l'assistant "Create EJB 3.x Session Bean" (Créer EJB 3.x Session Bean)

    Pour ouvrir l'assistant Créer EJB 3.x Session Bean, naviguer dans le menu File, et sélectionner New, puis Session Bean (EJB 3.x).
    Créer un assistant EJB 3.x Session Bean

    Figure 7.7. Créer un assistant EJB 3.x Session Bean

  3. Indiquer les informations de classe

    Fournir les informations suivantes :
    • Projet
      Verifier que le projet qui convient a bien été sélectionné.
    • Dossier source
      Il s'agit du dossier dans lequel les fichiers source de Java seront créés. Cela n'a pas normalement besoin d'être changé.
    • Package
      Indiquer le package auquel cette classe appartient.
    • Nom de la classe
      Indiquer le nom de la classe qui représentera le session bean.
    • Superclass
      La classe de session bean peut hériter d'une Superclass. Indiquer ici si votre session a une Superclass.
    • Type d'état
      Indiquer le type d'état du session bean: stateless, stateful, ou singleton.
    • Interfaces commerciales
      Par défaut, la case No-interface est cochée, donc aucune interface ne sera créée. Cocher les cases des interfaces que vous souhaitez définir et ajuster les noms si nécessaire.
      Nous vous rappelons que les beans Enterprise d'un WAR (Web Archive) ne supportent qu'EJB 3.1 Lite, et que cela n'inclut pas les interfaces commerciales distantes.
    Cliquer sur Next.
  4. Informations spécifiques aux Session Bean

    Vous pouvez saisir des informations supplémentaires ici pour personnaliser encore plus le session bean. Vous n'avez pas besoin de changer quoi que ce soit ici.
    Les items que vous pouvez changer sont :
    • Nom de bean.
    • Nom mappé.
    • Type de transaction (géré conteneur ou géré bean).
    • On peut fournir des interfaces supplémentaires que le bean doit implémenter.
    • Vous pouvez également spécifier des interfaces EJB 2.x Home et Component si nécessaire.
  5. Terminer

    Cliquer sur le bouton Finish et le nouveau session bean sera créé, et ajouté au projet. Les fichiers de toute nouvelle interface commerciale seront également créés, s'ils ont été spécifiés.
RÉSULTAT: un nouveau session bean sera ajouté au projet.
Nouveau Session Bean dans Red Hat JBoss Developer Studio

Figure 7.8. Nouveau Session Bean dans Red Hat JBoss Developer Studio

7.4. Message-Driven Beans

7.4.1. Message-Driven Beans

Les Message-driven Beans (MDB) fournissent un modèle basé-événement pour le développement de l'application. Les méthodes des MDB ne sont pas injectées ou invoquées du code client mais sont déclenchées par la réception de messages d'un service de messagerie comme le serveur Java Messaging Service (JMS). La spécification Java EE 6 exige que JMS soit pris en charge, mais les autres systèmes de messagerie peuvent être supportés également.

7.4.2. Adaptateurs de ressources

Un adaptateur de ressources est un composant Java EE déployable qui permet la communication entre une application Java EE et une entreprise d'informations système (EIE) à l'aide de la spécification Java Connector Architecture (JCA). Un adaptateur de ressources est souvent fourni par les fournisseurs de l'EIS pour permettre une intégration facile de leurs produits aux applications Java EE.
Un système d'information Enterprise peut être n'importe quel autre système de logiciel au sein d'une organisation. Les exemples incluent les systèmes ERP (Enterprise Resource Planning), les systèmes de base de données, les serveurs d'e-mails et les systèmes de messagerie propriétaires.
Un adaptateur de ressources est empaqueté dans un fichier de Ressources Adaptateur Archive (RAR) qui peut être déployé dans JBoss EAP 6. Un fichier RAR peut également être inclus dans un déploiement Enterprise Archive (EAR).

7.4.3. Créer un Message-Driven Bean basé JMS dans Red Hat JBoss Developer Studio

Cette procédure montre comment ajouter un Message-Driven Bean basé JMS dans Red Hat JBoss Developer Studio. Cette procédure crée un Message-Driven Bean EJB 3.x qui utilise des annotations.

Conditions préalables :

  1. Vous devrez avoir un projet existant ouvert dans Red Hat JBoss Developer Studio.
  2. Vous devrez connaître le nom et le type de la destination JMS que le bean écoutera.
  3. Le support pour JMS (Java Messaging Service) doit être autorisé dans la configuration JBoss EAP 6 dans laquelle ce bean sera déployé.

Procédure 7.6. Ajouter un Message-Driven Bean basé JMS dans Red Hat JBoss Developer Studio

  1. Ouvrir l'assistant Create EJB 3.x Message-Driven Bean

    Aller à FileNewOther. Sélectionner EJB/Message-Driven Bean (EJB 3.x) et cliquer sur le bouton Next.
    Créer l'assistant Message-Driven Bean EJB 3.x

    Figure 7.9. Créer l'assistant Message-Driven Bean EJB 3.x

  2. Indiquer les détails de la destination de fichier de classe

    Il existe trois ensembles de détails à indiquer pour la classe de bean : le projet, la classe Java et le message de destination.
    Projet
    • Si plusieurs projets existent dans Workspace, veillez à ce que le bon projet soit sélectionné dans le menu Project.
    • Le dossier où le fichier source du nouveau bean sera créé est ejbModule sous le répertoire du projet sélectionné. Ne peut être changé que pour répondre à des cas particuliers.
    Classe Java
    • Les champs obligatoires sont les suivants : Java Package et class name.
    • Il n'est pas nécessaire de fournir une Superclass à moins que la logique commerciale de votre application ne l'exige.
    Destination du message
    Voici les informations à fournir pour un Message-Driven Bean basé JMS :
    • Destination name. C'est la file d'attente ou le nom du sujet qui contient les messages auxquels le bean répondra.
    • La case JMS est sélectionnée par défaut. Veuillez ne pas la changer.
    • Définir Destination type comme Queue ou Topic selon ce que l'on vous demande.
    Cliquer sur le bouton Next.
  3. Saisir les informations spécifiques aux Message-Driven Bean

    Les valeurs par défaut sont appropriées pour un Message-Driven Bean basé JMS utilisant des transactions gérées conteneur.
    • Changer le type de transaction à Bean si le Bean doit utiliser des transactions Bean-managed (gérées bean).
    • Changer le nom du Bean s'il est demandé un nom de bean différent du nom de classe.
    • L'interface JMS Message Listener sera déjà listée. Vous n'aurez pas besoin d'ajouter ou de supprimer des interfaces, à moins qu'elles soient spécifiques à votre logique commerciale d'applications.
    • Laisser les cases décochées pour créer les méthodes stub sélectionnées.
    Cliquer sur le bouton Finish.
Résultat: le Bean Message-Driven sera créé avec des méthodes stub pour le contructeur par défaut et la méthode onMessage(). Une fenêtre d'édition de Red Hat JBoss Developer Studio est apparue avec le fichier correspondant.

7.4.4. Activer la substitution de propriété des EJB et MDB dans une application

Red Hat JBoss EAP vous permet maintenant d'activer la substitution de propriétés dans les EJB et les MDB à l'aide des annotations @ActivationConfigProperty et @Resource. La substitution de propriétés nécessite la configuration et les changements de code suivants :
  • Vous devez activer la substitution de propriété dans le fichier de configuration du serveur JBoss EAP.
  • Vous devez définir les propriétés système dans le fichier de configuration du serveur ou les passer comme arguments quand vous démarrez le serveur JBoss EAP.
  • Vous devez modifier le code pour utiliser les variables de substitution.

Procédure 7.7. Implémenter la substitution de propriétés dans une application MDB

Les exemples de code suivants sont basés sur le guide de démarrage rapide helloworld-mdb fourni dans JBoss EAP 6.3 ou version supérieure. Cette section vous montre comment modifier le guide de démarrage rapide pour activer la substitution de propriété.
  1. Configurer le serveur JBoss EAP pour activer la substituion de propriété.

    Le serveur JBoss EAP doit être configuré pour activer la substitution de propriété. Pour cela, définir l'attribut <annotation-property-replacement> qui se trouve dans le sous-système ee du fichier de configuration du serveur à true.
    1. Sauvegarder le fichier de configuration du serveur. L'exemple de guide de démarrage rapide helloworld-mdb nécessite un profil complet pour les serveurs autonomes, donc il s'agit du standalone/configuration/standalone-full.xml. Si vous exécutez votre serveur dans un domaine géré, il s'agira du fichier domain/configuration/domain.xml.
    2. Démarrer le serveur JBos EAP avec le profil complet.
      Dans Linux :
      EAP_HOME/bin/standalone.sh -c standalone-full.xml
      Dans Windows :
      EAP_HOMEbin\standalone.bat -c standalone-full.xml
    3. Lancer l'interface CLI par la commande pour votre système d'exploitation.
      Dans Linux :
      EAP_HOME/bin/jboss-cli.sh --connect
      Dans Windows :
      EAP_HOME\bin\jboss-cli.bat --connect
    4. Saisir la commande suivante pour activer la substitution de propriété d'annotation.
      /subsystem=ee:write-attribute(name=annotation-property-replacement,value=true) 
    5. Vous devriez voir apparaître le résultat suivant :
      {"outcome" => "success"}
    6. Réviser les changements apportés au fichier de configuration du serveur JBoss EAP. Le sous-système ee doit maintenant contenir l'XML suivant.
      <subsystem xmlns="urn:jboss:domain:ee:1.2">
          <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
          <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
          <annotation-property-replacement>true</annotation-property-replacement>
      </subsystem>
  2. Définir les propriétés système.

    Vous pouvez spécifier les propriétés du système dans le fichier de configuration du serveur ou vous pouvez les passer comme arguments de ligne de commande lorsque vous démarrez le serveur JBoss EAP. Les propriétés système définies dans le fichier de configuration de serveur ont la priorité sur celles passées sur la ligne de commande lorsque vous démarrez le serveur.
    • Vous devez définir les propriétés système dans le fichier de configuration.
      1. Démarrez le serveur JBoss EAP et le Management API comme indiqué dans les étapes précédentes.
      2. Utiliser la syntaxe de commande suivante pour configurer une propriété système dans le serveur JBoss EAP :
        /system-property=PROPERTY_NAME:add(value=PROPERTY_VALUE) 
        Pour le guide de démarrage rapide helloworld-mdb, nous configurons les propriétés système suivantes :
        /system-property=property.helloworldmdb.queue:add(value=java:/queue/HELLOWORLDMDBPropQueue)
        /system-property=property.helloworldmdb.topic:add(value=java:/topic/HELLOWORLDMDBPropTopic)
        /system-property=property.connection.factory:add(value=java:/ConnectionFactory)
      3. Réviser les changements apportés au fichier de configuration du serveur JBoss EAP. Les propriétés système suivantes doivent maintenant apparaître après les <extensions>.
        <system-properties>
            <property name="property.helloworldmdb.queue" value="java:/queue/HELLOWORLDMDBPropQueue"/>
            <property name="property.helloworldmdb.topic" value="java:/topic/HELLOWORLDMDBPropTopic"/>
            <property name="property.connection.factory" value="java:/ConnectionFactory"/>
        </system-properties>
    • Passez les propriétés système comme arguments de ligne de commande quand vous démarrez le serveur JBoss EAP sous la forme d'un -DPROPERTY_NAME=PROPERTY_VALUE. Ce qui suit est un exemple de la façon dont on passe les arguments dans les propriétés système définies dans l'étape précédente.
      EAP_HOME/bin/standalone.sh -c standalone-full.xml -Dproperty.helloworldmdb.queuejava:/queue/HELLOWORLDMDBPropQueue -Dproperty.helloworldmdb.topic=java:/topic/HELLOWORLDMDBPropTopic -Dproperty.connection.factory=java:/ConnectionFactory
  3. Modifier le code pour utiliser les substitutions de propriétés système.

    Remplacer les valeurs d'annotation @ActivationConfigProperty et @Resource codées en dur par des substitutions pour les propriétés système nouvellement définies. Voici des exemples sur la façon de changer le guide de démarrage rapide helloworld-mdb pour qu'il utilise les substitutions de propriétés système nouvellements définies pour les annotations qui se trouvent dans le code source.
    1. Modifier la valeur de propriété @ActivationConfigProperty destination qui se trouve dans la classe HelloWorldQueueMDB afin d'utiliser la substitution pour la propriété système. L'annotation @MessageDriven devra ressembler à ceci :
      @MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
          @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
          @ActivationConfigProperty(propertyName = "destination", propertyValue = "${property.helloworldmdb.queue}"),   
          @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
      
    2. Modifier la valeur de propriété @ActivationConfigProperty destination qui se trouve dans la classe HelloWorldTopicMDB afin d'utiliser la substitution pour la propriété système. L'annotation @MessageDriven devra ressembler à ceci :
      @MessageDriven(name = "HelloWorldQTopicMDB", activationConfig = {
          @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
          @ActivationConfigProperty(propertyName = "destination", propertyValue = "${property.helloworldmdb.topic}"),   
          @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
    3. Modifier les annotations @Resource qui se trouvent dans la classe HelloWorldMDBServletClient afin d'utiliser les substitutions de propriétés système. Le code devra ressembler à ceci :
      @Resource(mappedName = "${property.connection.factory}")
      private ConnectionFactory connectionFactory;
      
      @Resource(mappedName = "${property.helloworldmdb.queue}")   
      private Queue queue;
      
      @Resource(mappedName = "${property.helloworldmdb.topic}")
      private Topic topic;
      
    4. Modifier le fichier hornetq-jms.xml pour pouvoir utiliser les valeurs de substitution de propriétés système.
      <?xml version="1.0" encoding="UTF-8"?>
      <messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0">
          <hornetq-server>
              <jms-destinations>
                  <jms-queue name="HELLOWORLDMDBQueue">
                      <entry name="${property.helloworldmdb.queue}"/>
                  </jms-queue>
                  <jms-topic name="HELLOWORLDMDBTopic">
                      <entry name="${property.helloworldmdb.topic}"/>
                  </jms-topic>
              </jms-destinations>
          </hornetq-server>
      </messaging-deployment>
  4. Déployer l'application. L'application utilisera maintenant les valeurs indiquées par les propriétés système comme valeurs de propriété pour @Resource et @ActivationConfigProperty.

7.5. Invoquer les Session Beans

7.5.1. Invoquer un session bean à distance avec JNDI

Cette tâche décrit comment ajouter un support à un client distant pour l'invocation de session beans avec JNDI. La tâche assume que le projet est construit avec Maven.
Le Quickstart ejb-remote contient des projets Maven qui démontrent cette fonctionnalité. Le Quickstart contient des projets à la fois pour le déploiement des session beans et pour le client distant. Les exemples de code ci-dessous sont extraits du projet du client distant.
Cette tâche assume que les session beans n'ont pas besoin d'authentification.

Conditions préalables

Les conditions suivantes doivent être respectées avant de commencer :
  • Vous devez déjà avoir un projet Maven créé, prêt à l'utilisation.
  • La configuration du référentiel JBoss EAP 6 Maven a déjà été ajoutée.
  • Les session beans que vous souhaitez invoquer sont déjà déployés.
  • Les session beans déployés implémentent les interfaces commerciales éloignées.
  • Les interfaces commerciales éloignées des beans de session sont disponibles sous forme de dépendance de Maven. Si les interfaces commerciales éloignées ne sont disponibles que sous forme de fichier JAR, alors il est recommandé d'ajouter le JAR à votre référentiel Maven comme un artefact. Reportez-vous à la documentation de Maven pour obtenir des directives install:install-file, http://maven.apache.org/plugins/maven-install-plugin/usage.html
  • Vous aurez besoin de connaître le nom d'hôte et le port JNDI du serveur qui hébergent les session beans.
Pour invoquer un session bean d'un client distant, vous devez tout d'abord configurer le projet correctement.

Procédure 7.8. Ajouter une configuration de projet Maven pour l'invocation à distance des session beans

  1. Ajouter les dépendances de projet utiles

    Le pom.xml du projet doit être mis à jour pour pouvoir inclure les dépendances nécessaires.
  2. Ajouter le fichier jboss-ejb-client.properties

    L'API du client JBoss EJB s'attend à trouver un fichier dans le root du projet nommé jboss-ejb-client.properties qui contient les informations de connexion aux services JNDI. Ajouter ce fichier au répertoire src/main/resources/ de votre projet avec le contenu suivant.
    # In the following line, set SSL_ENABLED to true for SSL
    remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
    remote.connections=default
    # Uncomment the following line to set SSL_STARTTLS to true for SSL
    # remote.connection.default.connect.options.org.xnio.Options.SSL_STARTTLS=true
    remote.connection.default.host=localhost
    remote.connection.default.port = 4447
    remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
    # Add any of the following SASL options if required
    # remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
    # remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT=false
    # remote.connection.default.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS=JBOSS-LOCAL-USER
    
    Changer le nom d'hôte et le port pour qu'ils correspondent au serveur. 4447 est le numéro de port par défaut. Pour une connexion sécurisée, définir la ligne SSL_ENABLED à true et dé-commenter la ligne SSL_STARTTLS. L'interface éloignée du conteneur supporte les connexions sécurisées et non sécurisées en utilisant le même port.
  3. Ajouter des dépendances aux interfaces commerciales à distance.

    Ajouter les dépendances Maven au pom.xml aux interfaces commerciales éloignées des session beans.
    <dependency>
       <groupId>org.jboss.as.quickstarts</groupId>
       <artifactId>jboss-ejb-remote-server-side</artifactId>
       <type>ejb-client</type>
       <version>${project.version}</version>
    </dependency>
Maintenant que le projet a été configuré correctement, vous pouvez ajouter le code pour accéder et invoquer les session beans.

Procédure 7.9. Obtenez un Bean Proxy par JNDI et invoquez les méthodes du Bean

  1. Exceptions vérifiées par Handle

    Deux des méthodes utilisées dans le code suivant (InitialContext() et lookup()) ont une exception vérifiée du type javax.naming.NamingException. Ces appels de méthode doivent soit être contenus dans un bloc try/catch qui intercepte NamingException ou dans une méthode déclarée pour lancer NamingException. Le Quickstart ejb-remote utilise la seconde technique.
  2. Créer un contexte JNDI

    Un objet de contexte JNDI fournit le mécanisme pour demander les ressources dans le serveur. Créer un contexte JNDI avec le code suivant :
    final Hashtable jndiProperties = new Hashtable();
    jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
    final Context context = new InitialContext(jndiProperties);
    Les propriétés de connexion du service JNDI sont lues à partir du fichier jboss-ejb-client.properties.
  3. Utiliser la méthode JNDI Context's lookup() pour obtenir un Bean Proxy

    Invoquer le méthode lookup() du bean proxy et lui faire passer le nom JNDI du session bean dont vous avez besoin. Un objet sera renvoyé et il devra correspondre au type de méthode d'interface commerciale qui contient les méthodes que vous souhaitez invoquer.
    
    final RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
        "ejb:/jboss-ejb-remote-server-side//CalculatorBean!" + 
        RemoteCalculator.class.getName());
    
    Les noms de session bean JNDI sont définis par une syntaxe particulière. Pour plus d'informations, consulter Section 7.8.1, « Référence de nommage EJB JNDI » .
  4. Méthodes d'invocation

    Maintenant que vous avez un objet bean proxy, vous pouvez invoquer n'importe quelle méthode qui contient l'interface commerciale distante.
    int a = 204;
    int b = 340;
    System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
    int sum = statelessRemoteCalculator.add(a, b);
    System.out.println("Remote calculator returned sum = " + sum);
    Le proxy bean transmet la demande d'invocation de méthode au bean de session sur le serveur, où elle est exécutée. Le résultat est retourné au proxy bean qui, ensuite, le retourne à l'appelant. La communication entre le proxy bean et le bean de session distant est transparente à l'appelant.
Vous devriez maintenant être en mesure de configurer un projet Maven pour soutenir les sessions beans invoquant de session sur un serveur distant et écrire le code d'invocation des méthodes de sessions beans à l'aide d'un proxy bean récupéré du serveur par JNDI.

7.5.2. Contextes Client EJB

JBoss EAP 6 a introduit l'API client EJB pour gérer les invocations EJB à distance. L'API client EJB JBoss utilise le Contexte Client EJB, qui peut être associé à, et être utilisé par un ou plusieurs threads simultanément. Cela signifie qu'un Contexte Client EJB pourrait contenir n'importe quel nombre de récepteurs EJB. Un récepteur EJB est un composant qui sait communiquer avec un serveur capable de gérer l'invocation EJB.
  • Un client à distance, exécuté comme application Java autonome.
  • Un client à distance, exécuté dans une autre instance de JBoss EAP 6.
Selon le type de client à distance, d'un point de vue API Client EJB, il pourrait y avoir plus d'un Context Client EJB au sein de la JVM.
Bien que les applications autonomes possèdent généralement un Contexte Client EJB unique pouvant être sauvegardé par n'importe quel nombre de récepteurs EJB, cela n'est pas obligatoire. Si une application autonome possède plus d'un Contexte Client EJB, un sélecteur de Contexte Client EJB sera chargé de retourner le contexte approprié.
Dans le cas où des clients à distance s'exécutent dans une autre instance de JBoss EAP 6, chaque application déployée aura un contexte client EJB correspondant. A chaque fois que cette application invoque un autre EJB, le contexte client EJB correspondant est utilisé pour trouver le récepteur EJB correct, qui gère ensuite l'invocation.

7.5.3. Considérations lors de l'utilisation d'un Contexte EJB Unique

Résumé

Vous devez considérer les conditions préalables de votre application lors de l'utilisation d'un contexte client EJB unique avec des clients à distance autonomes. Pour plus d'informations sur les différents types de clients à distance, veuillez consulter : Section 7.5.2, « Contextes Client EJB » .

Procédure typique pour un Client Autonome à distance avec un Contexte Client EJB unique.

Un client autonome à distance possède généralement un seul contexte client EJB sauvegardé par n'importe quel nombre de récepteurs EJB. Voici un exemple d'application client à distance autonome :

public class MyApplication {
    public static void main(String args[]) {
        final javax.naming.Context ctxOne = new javax.naming.InitialContext();
        final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
        beanOne.doSomething();
        ...
    }
}

Les recherches JNDI de client à distance sont généralement sauvegardées par un fichier jboss-ejb-client.properties utilisé pour installer le contexte client EJB et les récepteurs EJB. Cette configuration comprend également les informations de sécurité, qui sont ensuite utilisées pour créer le récepteur EJB qui relie le serveur JBoss EAP 6. Quand le code ci-dessus est invoqué, l'API client EJB recherche le contexte client EJB, ensuite utilisé pour sélectionner le récepteur EJB qui recevra et traitera la requête d'invocation EJB. Dans ce cas, il n'y a que le contexte client EJB unique, donc ce contexte sera utilisé par le code ci-dessus pour invoquer le bean. La procédure pour invoquer un bean de session à distance en utilisant JNDI est décrite plus en détails ici : Section 7.5.1, « Invoquer un session bean à distance avec JNDI » .
Client autonome à distance nécessitant différentes informations

Une application d'utilisateur pourrait vouloir invoquer un bean plus d'une fois, mais se connecter au serveur JBoss EAp 6 en utilisant différentes informations de sécurité. Voici un exemple d'une application client à distance autonome qui invoque deux fois le même bean :

public class MyApplication {
    public static void main(String args[]) {
        // Use the "foo" security credential connect to the server and invoke this bean instance
        final javax.naming.Context ctxOne = new javax.naming.InitialContext();
        final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
        beanOne.doSomething();
        ...
 
        // Use the "bar" security credential to connect to the server and invoke this bean instance
        final javax.naming.Context ctxTwo = new javax.naming.InitialContext();
        final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
        beanTwo.doSomething();
        ...
    }
}

Dans ce cas, l'application veut se connecter à la même instance de serveur pour invoquer l'EJB hébergé sur ce serveur, mais veut utiliser deux informations différentes pour se connecter au serveur. Puisque l'application du client possède un contexte de client EJB unique, qui peut avoir seulement un récepteur EJB pour chaque instance de serveur, cela signifie que le code ci-dessus n'utilise qu'une seule information pour se connecter au serveur et que le code ne s'exécute pas comme il le devrait.
Solution

Les contextes client EJB scoped offrent une solution à ce problème. Ils offrent un plus grand contrôle sur les contextes client EJB et leurs contextes JNDI associés, qui sont généralement utilisés pour les invocations EJB. Pour plus d'informations sur les contextes client EJB scoped, veuillez consulter Section 7.5.4, « Utiliser des Contextes Client EJB scoped » et Section 7.5.5, « Configurer les EJB en utilisant un contexte client EJB scoped » .

7.5.4. Utiliser des Contextes Client EJB scoped

Résumé

Pour invoquer un EJB dans les versions antérieures de JBoss EAP 6, il faut généralement créer un contexte JNDI et lui passer le PROVIDER_URL, qui indique le serveur de cible. Toute invocation faite sur les proxys EJB recherchées en utilisant ce contexte JNDI finiraient sur ce serveur. Dans les contextes client EJB scoped, les applications utilisateur contrôlent quel récepteur EJB est utilisé pour une invocation spécifique.

Utiliser le Contexte Client EJB scoped dans un client autonome à distance

Avant l'introduction des contextes client EJB étendus, le contexte était généralement étendu à l'application client. Les contextes client étendus autorisent maintenant les contextes client EJB à être étendus avec les contextes JNDI. Voici un exemple d'une application client autonome à distance qui invoque le même bean deux fois en utilisant un contexte client EJB scoped.

public class MyApplication { 
    public static void main(String args[]) {
 
        // Use the "foo" security credential connect to the server and invoke this bean instance
        final Properties ejbClientContextPropsOne = getPropsForEJBClientContextOne():
        final javax.naming.Context ctxOne = new javax.naming.InitialContext(ejbClientContextPropsOne);
        final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
        beanOne.doSomething();
        ...
        ctxOne.close();
 
        // Use the "bar" security credential to connect to the server and invoke this bean instance
        final Properties ejbClientContextPropsTwo = getPropsForEJBClientContextTwo():
        final javax.naming.Context ctxTwo = new javax.naming.InitialContext(ejbClientContextPropsTwo);
        final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
        beanTwo.doSomething();
        ...
        ctxTwo.close();
    }
}

Pour utiliser le contexte client EJB scoped, configurer les propriétés client EJB par programmation et passer les propriétés sur la création de contexte. Les propriétés correspondent au même ensemble de propriétés que celles qui sont utilisées dans le fichier standard jboss-ejb-client.properties. Pour étendre le contexte client EJB au contexte JNDI, il faut également indiquer la propriété org.jboss.ejb.client.scoped.context et définir sa valeur sur true. Cette propriété notifie à l'API client EJB qu'il doit créer un contexte client EJB, sauvegardé par des récepteurs EJB, et que le contexte créé doit ensuite être étendu ou visible uniquement au contexte JNDI qui l'a créé. Tout proxy EJB recherché ou invoqué en utilisant ce contexte JNDI ne connaîtra que le contexte client EJB associé à ce contexte JNDI. Les autres contextes JNDI utilisés par l'application pour rechercher ou invoquer des EJB ne reconnaîtra pas les autres contextes client EJB scoped.
Les contextes JNDI qui ne passent pas les propriétés org.jboss.ejb.client.scoped.context et ne sont pas étendus vers un contexte client EJB, utiliseront le comportement par défaut, qui utilise le contexte client EJB existant normalement attaché à l'application dans sa totalité.
Les contextes client EJB scoped donnent aux applications d'utilisateur la même souplesse qui caractérisait les invocations JNDI basées JNP dans les versions précédentes de JBoss EAP 6. Ils donnent aux applications utilisateurs un plus grand contrôle sur le contexte JNDI communique avec un serveur et sur comment il se connecte à ce serveur.

Note

Dans le contexte scoped, les ressources sous-jacentes ne sont plus gérées par le conteneur ou l'API, donc vous devez fermer l'InitialContext lorsqu'il n'est plus utile. Lorsque l'InitialContext est fermé, les ressources sont libérées immédiatement. Les proxys qui y sont liées ne sont plus valides et tout invocation soulèvera une exception. Une mauvaise fermeture du InitialContext peut entraîner les problèmes de ressource ou de performance.

7.5.5. Configurer les EJB en utilisant un contexte client EJB scoped

Résumé

Les EJB peuvent être configurés en utilisant un contexte scoped basé mappe. Cela est possible en complétant une mappe Properties par programmation en utilisant les propriétés standards trouvées dans jboss-ejb-client.properties, en indiquant true pour la propriété org.jboss.ejb.client.scoped.context, et en passant les propriétés sur la création InitialContext.

L'avantage d'utiliser un contexte scoped est qu'il vous permet de configurer l'accès sans référencer directement l'EJB ou importer les classes JBoss. Il permet également de configurer et de répartir la charge d'un hôte dans un environnement multithreadé lors du runtime.

Procédure 7.10. Configurer un EJB en utilisant un contexte scoped basé mappage

  1. Configurer les propriétés

    Configurer les propriétés client EJB par programmation, en indiquant le même ensemble de propriétés utilisées dans le fichier jboss-ejb-client.properties standard. Pour activer le contexte scoped, vous devez indiquer la propriété org.jboss.ejb.client.scoped.context et configurer sa valeur sur true. Voici un exemple de configuration des propriétés par programmation :
    // Configure  EJB Client properties for the InitialContext
    Properties ejbClientContextProps = new Properties();
    ejbClientContextProps.put(“remote.connections”,”name1”);
    ejbClientContextProps.put(“remote.connection.name1.host”,”localhost”);
    ejbClientContextProps.put(“remote.connection.name1.port”,”4447”);
    // Property to enable scoped EJB client context which will be tied to the JNDI context
    ejbClientContextProps.put("org.jboss.ejb.client.scoped.context", “true”);
    
  2. Passer les propriétés en création de contexte

    // Create the context using the configured properties
    InitialContext ic = new InitialContext(ejbClientContextProps);
    MySLSB bean = ic.lookup("ejb:myapp/ejb//MySLSBBean!" + MySLSB.class.getName());
    
Informations supplémentaires

  • Les contextes générés par les proxys EJB de recherche sont liés par ce contexte étendu et n'utilisent que les paramètres de connexion appropriés. Cela permet de créer différents contextes pour accéder aux données au sein d'une application client ou pour accéder aux serveurs de manière indépendante en utilisant différents logins.
  • Dans le client, le InitialContext étendu et le proxy étendu sont tous les deux transférés vers les threads, permettant à chaque thread de fonctionner avec le contexte donné. Il est également possible de transférer le proxy vers des threads multiples qui peuvent l'utiliser simultanément.
  • Le contexte étendu proxy EJB est sérialisé sur l'appel distant, puis désérialisé sur le serveur. Lorsqu'il est désérialisé, les informations de contexte étendu sont supprimées et il retourne à son état par défaut. Si le proxy désérialisé est utilisé sur le serveur distant, c-a-d s'il n'a plus le contexte étendu utilisé lors de sa création, cela peut provoquer une erreur EJBCLIENT000025 ou éventuellement appeler une cible indésirable en utilisant le nom de l'EJB.

7.5.6. Propriétés Client EJB

Résumé

Les tableaux suivants énumèrent les propriétés pouvant être configurées par programmation ou dans le fichier jboss-ejb-client.properties.

Propriétés Globales de Client EJB

Le tableau suivant énumère les propriétés valides pour la bibliothèque entière au sein de la même étendue.

Tableau 7.1. Propriétés globales

Nom de propriété Description
endpoint.name
Nom du point de terminaison du client. Si pas configuré, sa valeur par défaut sera client-endpoint.
Cela peut s'avérer utile pour distinguer différentes configurations de point de terminaison puisque le nom de thread comprend cette propriété.
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED
Valeur booléenne qui indique si le protocole SSL est activé pour toutes les connexions.
deployment.node.selector
Nom complet de l'implémentation de org.jboss.ejb.client.DeploymentNodeSelector.
Ceci est utilisé pour équilibrer les charges de l'invocation des EJB.
invocation.timeout
Délai pour la liaison EJB ou le cycle requête/réponse de l'invocation de la méthode. Valeur exprimée en millisecondes.
L'invocation de toute méthode lève une java.util.concurrent.TimeoutException si l'exécution prend plus de temps que la période de délai. L'exécution se termine et le serveur n'est pas interrompu.
reconnect.tasks.timeout
Délai des tâches de reconnexion de l'arrière-plan. Valeur exprimée en millisecondes.
Si des connexions sont éteintes, la prochaine invocation EJB de client utilisera un algorithme pour décider si une reconnexion est nécessaire pour trouver le bon nœud.
org.jboss.ejb.client.scoped.context
Valeur booléenne indiquant s'il faut activer le contexte client EJB scoped. La valeur par défaut est false.
Si défini comme true, le client EJB utilisera le contexte étendu (scoped) lié au contexte JNDI. Sinon, le contexte de client EJB utilisera la sélecteur global dans le JVM pour déterminer les propriétés utilisées pour appeler l'hôte et l'EJB à distance.
Propriétés de connexion de client EJB

Les propriétés de connexion commencent par le préfixe remote.connection.CONNECTION_NAME où le CONNECTION_NAME est un identifiant local utilisé uniquement pour identifier la connexion de manière exclusive.

Tableau 7.2. Propriétés de connexion

Nom de propriété Description
remote.connections
Liste de connection-names actifs séparés par des virgules. Chaque connexion est configurée sous ce nom.
remote.connection.CONNECTION_NAME.host
Nom d'hôte ou IP de cette connexion
remote.connection.CONNECTION_NAME.port
Port de la connexion. La valeur par défaut est 4447.
remote.connection.CONNECTION_NAME.username
Nom d'utilisateur utilisé pour authentifier la sécurité de connexion.
remote.connection.CONNECTION_NAME.password
Mot de passe utilisé pour authentifier l'utilisateur.
remote.connection.CONNECTION_NAME.connect.timeout
Période de délai de la connexion initiale. Après cela, la tâche de reconnexion vérifiera périodiquement si la connexion peut être établie. Valeur exprimée en millisecondes.
remote.connection.CONNECTION_NAME.callback.handler.class
Nom complet de la classe CallbackHandler. Sera utilisé pour établir la connexion et ne peut être changé tant que la connexion sera ouverte.
remote.connection.CONNECTION_NAME.
channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES
Valeur entière indiquant le nombre maximale de requêtes sortantes. La valeur par défaut est 80.
Il n'y a qu'une connexion entre le client (JVM) et le serveur pour gérer les invocations.
remote.connection.CONNECTION_NAME.
connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS
Valeur booléenne déterminant si les identifiants doivent être fournis par le client pour réussir la connexion. La valeur par défaut est true.
Si défini comme true, le client doit fournir les identifiants. Si défini comme false, l'invocation est autorisée tant que le connecteur à distance ne demande pas un domaine de sécurité.
remote.connection.CONNECTION_NAME.
connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS
Désactive certains mécanismes SASL utilisés pour l'authentification pendant la création de la connexion.
JBOSS_LOCAL_USER signifie que le mécanisme d'authentification silencieuse, utilisé lorsque le client et le serveur sont sur la même machine, est désactivé.
remote.connection.CONNECTION_NAME.
connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT
Valeur booléenne qui active ou désactive l'utilisation de messages de texte brut pendant l'authentification. Si JAAS est utilisé, il doit être défini comme « false » pour autoriser un mot de passe de texte brut.
remote.connection.CONNECTION_NAME.
connect.options.org.xnio.Options.SSL_ENABLED
Valeur booléenne indiquant si le protocole SSL est activé pour cette connexion.
remote.connection.CONNECTION_NAME.
connect.options.org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL
Intervalle pour envoyer une pulsation entre le client et le serveur afin de prévenir les fermetures automatiques, dans le cas d'un pare-feu par exemple. Valeur exprimée en millisecondes.
Propriétés de cluster de client EJB

Si la connexion initiale se connecte à un environnement clusterisé, la topologie du cluster est reçue de manière automatique et asynchrone. Ces propriétés sont utilisées pour se connecter à chaque membre reçu. Chaque propriété commence avec le préfixe remote.cluster.CLUSTER_NAME où le CLUSTER_NAME se réfère à la configuration du sous-système Infinispan du serveur associé.

Tableau 7.3. Propriétés de Cluster

Nom de propriété Description
remote.cluster.CLUSTER_NAME.
clusternode.selector
Le nom complet de l'implémentation de org.jboss.ejb.client.ClusterNodeSelector.
Cette classe, plutôt que org.jboss.ejb.clientDeploymentNodeSelector, est utilisée pour équilibrer la charge des invocations EJB dans un environnement clusterisé. Si le cluster est complètement arrêté, l'invocation échouera avec comme message No ejb receiver available.
remote.cluster.CLUSTER_NAME.
channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES
Valeur entière indiquant le nombre maximale de requêtes sortantes pouvant être faites au cluster entier.
remote.cluster.CLUSTER_NAME.
node.NODE_NAME. channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES
Valeur entière indiquant le nombre maximale de requêtes sortantes pouvant être faites à ce nœud de cluster spécifique.

7.6. Intercepteurs de conteneurs

7.6.1. Intercepteurs de conteneurs

Les intercepteurs standard Java EE, définis dans la spécification JSR 318, Enterprise JavaBeans 3.1 sont sensés exécuter une fois que le conteneur a complété la propagation de contexte de sécurité, la gestion des transactions, et que d'autres conteneurs ont fourni le traitement de l'invocation. C'est un problème si l'application doit intercepter un appel avant qu'un intercepteur spécifique de conteneur soit exécuté.
Les versions antérieures de JBoss EAP 6.0 fournissent un moyen de connecter des intercepteurs côté serveur dans le flux de l'invocation, donc vous pourriez exécuter la logique de l'application spécifique avant que le conteneur ne termine le traitement de l'invocation. Cette fonctionnalité existait déjà dans JBoss EAP 6.1. Cela permet aux intercepteurs standards de Java EE d'être utilisés comme des intercepteurs de conteneur, ce qui signifie qu'ils utilisent les mêmes éléments XSD que ceux autorisés dans le fichier ejb-jar.xml pour la version 3.1 du descripteur de déploiement ejb-jar.
Positionnement de l'intercepteur du conteneur dans la chaîne d'intercepteur

Les intercepteurs de conteneur configurés pour un EJB sont certains d'être exécutés avant que le JBoss EAP ne fournisse des intercepteurs de sécurité, des intercepteurs de gestion de transaction ou autres intercepteurs fournis par le serveur. Cela permet aux intercepteurs de conteneurs spécifiques à l'application de traiter ou de configurer des données de contexte pertinentes avant l'invocation.

Différences entre l'intercepteur de conteneur et l'API Java EE Interceptor

Bien que les intercepteurs de conteneur soient modélisés pour pouvoir ressembler aux intercepteurs de Java EE, il y a quelques différences dans la sémantique de l'API. Par exemple, il est illégal pour les intercepteurs de conteneur d'invoquer la méthode javax.interceptor.InvocationContext.getTarget() parce que ces intercepteurs sont invoqués bien avant que les composants EJB ne soient configurés ou instanciés.

7.6.2. Créer une classe d'intercepteur de conteneur

Résumé

Les classes d'intercepteur de conteneur sont de simples POJO (Plain Old Java Objects). Ils utilisent @javax.annotation.AroundInvoke pour marquer la méthode qui est invoquée lors de l'invocation du bean.

Voici un exemple de classe d'intercepteur de conteneur qui marque la méthode iAmAround de l'invocation :

Exemple 7.1. Exemple de classe d'intercepteur de conteneur


public class ClassLevelContainerInterceptor {
    @AroundInvoke
    private Object iAmAround(final InvocationContext invocationContext) throws Exception {
        return this.getClass().getName() + " " + invocationContext.proceed();
    }
}
Pour obtenir un exemple de fichier de descripteur d'intercepteur de conteneur configuré pour utiliser cette classe, voir le fichier jboss-ejb3.xml décrit ici : Section 7.6.3, « Configurer un intercepteur de conteneur ».

7.6.3. Configurer un intercepteur de conteneur

Résumé

Les intercepteurs de conteneurs utilisent les bibliothèques d'intercepteur J2EE standard, ce qui signifie qu'ils utilisent les mêmes éléments XSD que ceux qui sont autorisés dans le fichier ejb-jar.xml pour la version 3.1 du descripteur de déploiement ejb-jar. Comme ils se reposent sur les bibliothèques standard d'interceptor Jave EE, les intercepteurs de conteneur peuvent uniquement être configurés à l'aide de descripteurs de déploiement. Cela a été conçu pour que les applications n'exigent pas une annotation spécifique de JBoss ou autre dépendance de bibliothèque. Pour plus d'informations sur les intercepteurs de conteneur, voir : Section 7.6.1, « Intercepteurs de conteneurs ».

Procédure 7.11. Créer le fichier de descripteur pour configurer l'intercepteur de conteneur

  1. Créer un fichier jboss-ejb3.xml dans le répertoire META-INF du déploiement EJB.
  2. Configurer les éléments de l'intercepteur du conteneur dans le fichier du descripteur.
    1. Utiliser l'espace-nom urn:container-interceptors:1.0 pour indiquer la configuration des éléments de l'intercepteur du conteneur.
    2. Utiliser l'élément <container-interceptors> pour indiquer les intercepteurs du conteneur.
    3. Utiliser les éléments <interceptor-binding> pour relier l'intercepteur du conteneur aux EJB. Les intercepteurs peuvent être reliés d'une des manières suivantes :
      • Relier l'intercepteur à tous les EJB du déploiement par le caractère générique *.
      • Relier l'intercepteur au niveau bean individuel par le nom spécifique de l'EJB.
      • Relier l'intercepteur au niveau de méthode spécifique des EJB.

      Note

      Ces éléments sont configurés par EJB 3.1 XSD de la même façon que pour les intercepteurs Java EE.
  3. Vérifier les exemples d'éléments ci-dessus dans le fichier de descripteur suivant.

    Exemple 7.2. jboss-ejb3.xml

    <jboss xmlns="http://www.jboss.com/xml/ns/javaee"
           xmlns:jee="http://java.sun.com/xml/ns/javaee"
           xmlns:ci ="urn:container-interceptors:1.0">
     
        <jee:assembly-descriptor>
            <ci:container-interceptors>
                <!-- Default interceptor -->
                <jee:interceptor-binding>
                    <ejb-name>*</ejb-name>
                    <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class>
                </jee:interceptor-binding>
                <!-- Class level container-interceptor -->
                <jee:interceptor-binding>
                    <ejb-name>AnotherFlowTrackingBean</ejb-name>
                    <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class>
                </jee:interceptor-binding>
                <!-- Method specific container-interceptor -->
                <jee:interceptor-binding>
                    <ejb-name>AnotherFlowTrackingBean</ejb-name>
                    <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class>
                    <method>
                        <method-name>echoWithMethodSpecificContainerInterceptor</method-name>
                    </method>
                </jee:interceptor-binding>
                <!-- container interceptors in a specific order -->
                <jee:interceptor-binding>
                    <ejb-name>AnotherFlowTrackingBean</ejb-name>
                    <interceptor-order>
                        <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class>
                        <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class>
                        <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class>
                    </interceptor-order>
                    <method>
                        <method-name>echoInSpecificOrderOfContainerInterceptors</method-name>
                    </method>
                </jee:interceptor-binding>
            </ci:container-interceptors>
        </jee:assembly-descriptor>
    </jboss>
    
    
    L'XSD de l'espace-nom urn:container-interceptors:1.0 se trouve dans EAP_HOME/docs/schema/jboss-ejb-container-interceptors_1_0.xsd.

7.6.4. Modifier l'identité du contexte de sécurité

Résumé

Par défaut, lorsque vous effectuez un appel distant à un EJB déployé sur le serveur d'applications, la connexion au serveur est authentifiée et toute demande reçue via cette connexion sera exécutée en tant qu'identité ayant authentifié la connexion. Cela est vrai pour les appels client-serveur et serveur-serveur. Si vous avez besoin d'utiliser différentes identités en provenance d'un même client, vous devrez normalement ouvrir des connexions multiples au serveur afin que chacune d'entre elles soient authentifiée en tant qu'identité différente. Plutôt que d'ouvrir plusieurs connexions de client, vous pouvez donner l'autorisation à l'utilisateur authentifié d'exécuter une requête sous un nom différent.

Cette section décrit comment changer d'identité sur une connexion client existante. Les exemples de code suivants sont des versions abrégées du code du Quickstart. Voir le Quickstart ejb-security-interceptors pour obtenir un exemple complet.

Procédure 7.12. Modifier l'identité du contexte de sécurité

Pour modifier l'identité d'une connexion sécurisée, vous devez créer les 3 composants suivants.
  1. Créer l'intercepteur côté client

    Cet intercepteur côté client doit implémenter l'interface org.jboss.ejb.client.EJBClientInterceptor. L'intercepteur doit passer l'identité requise par le mappage de données contextuelles, par l'intermédiaire d'un appel EJBClientInvocationContext.getContextData(). Voici un exemple de code d'intercepteur côté client  :
    public class ClientSecurityInterceptor implements EJBClientInterceptor {
    
        public void handleInvocation(EJBClientInvocationContext context) throws Exception {
            Principal currentPrincipal = SecurityActions.securityContextGetPrincipal();
    
            if (currentPrincipal != null) {
                Map<String, Object> contextData = context.getContextData();
                contextData.put(ServerSecurityInterceptor.DELEGATED_USER_KEY, currentPrincipal.getName());
            }
            context.sendRequest();
        }
    
        public Object handleInvocationResult(EJBClientInvocationContext context) throws Exception {
            return context.getResult();
        }
    }
    
    Les applications utilisateur peuvent insérer l'intercepteur dans la chaîne d'intercepteur dans le EJBClientContext par l'un des moyens suivants  :
    • Par programmation

      Par cette approche, vous appelez la méthode org.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor) et passez l'instance order et l'instance interceptor. L'instance order est utilisé pour déterminer où exactement cet intercepteur est placé dans la chaîne d'intercepteur.
    • Mécanisme ServiceLoader

      Cette approche nécessite la création d'un fichier META-INF/services/org.jboss.ejb.client.EJBClientInterceptor et de le placer ou le mettre dans le chemin de classe de l'application client. Les règles du fichier sont dictées par le Java ServiceLoader Mechanism. Ce fichier doit contenir, dans chaque ligne distincte, le nom de classe qualifié complet de l'implémentation d'intercepteur de client EJB. Les classes d'intercepteur de client EJB doivent être disponibles dans le chemin de classe. Les intercepteurs de client EJB ajoutés en utilisant le mécanisme de ServiceLoader sont ajoutés à la fin de la chaîne d'intercepteur de client, dans l'ordre dans lequel ils se trouvent dans le chemin de classe (classpath). Le démarrage rapide ou quickstart ejb-security-interceptors utilise cette approche.
  2. Créer et configurer l'intercepteur du conteneur côté serveur

    Les classes d'intercepteur de conteneur sont de simples Plain Old Java Objects (POJOs). Elles utilisent @javax.annotation.AroundInvoke pour marquer la méthode qui sera invoquée lors de l'invocation sur le bean. Pour plus d'informations sur les intercepteurs de conteneur, consulter : Section 7.6.1, « Intercepteurs de conteneurs ».
    1. Créer l'intercepteur de conteneur

      Cet intercepteur reçoit le InvocationContext en même temps que l'identité et demande le changement d'identité nouvelle. Ce qui suit est une version modifiée de l'exemple de code  :
          public class ServerSecurityInterceptor {
      
          private static final Logger logger = Logger.getLogger(ServerSecurityInterceptor.class);
      
          static final String DELEGATED_USER_KEY = ServerSecurityInterceptor.class.getName() + ".DelegationUser";
      
          @AroundInvoke
          public Object aroundInvoke(final InvocationContext invocationContext) throws Exception {
              Principal desiredUser = null;
              UserPrincipal connectionUser = null;
      
              Map<String, Object> contextData = invocationContext.getContextData();
              if (contextData.containsKey(DELEGATED_USER_KEY)) {
                  desiredUser = new SimplePrincipal((String) contextData.get(DELEGATED_USER_KEY));
      
                  Collection<Principal> connectionPrincipals = SecurityActions.getConnectionPrincipals();
      
                  if (connectionPrincipals != null) {
                      for (Principal current : connectionPrincipals) {
                          if (current instanceof UserPrincipal) {
                              connectionUser = (UserPrincipal) current;
                              break;
                          }
                      }
      
                  } else {
                      throw new IllegalStateException("Delegation user requested but no user on connection found.");
                  }
              }
      
      
              ContextStateCache stateCache = null;
              try {
                  if (desiredUser != null && connectionUser != null
                      && (desiredUser.getName().equals(connectionUser.getName()) == false)) {
                      // The final part of this check is to verify that the change does actually indicate a change in user.
                      try {
                          // We have been requested to use an authentication token
                          // so now we attempt the switch.
                          stateCache = SecurityActions.pushIdentity(desiredUser, new OuterUserCredential(connectionUser));
                      } catch (Exception e) {
                          logger.error("Failed to switch security context for user", e);
                          // Don't propagate the exception stacktrace back to the client for security reasons
                          throw new EJBAccessException("Unable to attempt switching of user.");
                      }
                  }
      
                  return invocationContext.proceed();
              } finally {
                  // switch back to original context
                  if (stateCache != null) {
                      SecurityActions.popIdentity(stateCache);;
                  }
              }
          }
      
    2. Configurer l'intercepteur du conteneur

      Pour plus d'informations sur la façon de configurer les intercepteurs de conteneurs côté serveur, consulter : Section 7.6.3, « Configurer un intercepteur de conteneur ».
  3. Créer le JAAS LoginModule

    Ce composant doit vérifier que l'utilisateur est en mesure d'exécuter les requêtes selon l'identité de la requête. Les exemples de code abrégés suivants indiquent les méthodes de login et de validation :
        @SuppressWarnings("unchecked")
        @Override
        public boolean login() throws LoginException {
            if (super.login() == true) {
                log.debug("super.login()==true");
                return true;
            }
    
            // Time to see if this is a delegation request.
            NameCallback ncb = new NameCallback("Username:");
            ObjectCallback ocb = new ObjectCallback("Password:");
    
            try {
                callbackHandler.handle(new Callback[] { ncb, ocb });
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                }
                return false; // If the CallbackHandler can not handle the required callbacks then no chance.
            }
    
            String name = ncb.getName();
            Object credential = ocb.getCredential();
    
            if (credential instanceof OuterUserCredential) {
                // This credential type will only be seen for a delegation request, if not seen then the request is not for us.
    
                if (delegationAcceptable(name, (OuterUserCredential) credential)) {
    
                    identity = new SimplePrincipal(name);
                    if (getUseFirstPass()) {
                        String userName = identity.getName();
                        if (log.isDebugEnabled())
                            log.debug("Storing username '" + userName + "' and empty password");
                        // Add the username and an empty password to the shared state map
                        sharedState.put("javax.security.auth.login.name", identity);
                        sharedState.put("javax.security.auth.login.password", "");
                    }
                    loginOk = true;
                    return true;
                }
            }
    
            return false; // Attempted login but not successful.
        }
    
        protected boolean delegationAcceptable(String requestedUser, OuterUserCredential connectionUser) {
        if (delegationMappings == null) {
            return false;
        }
    
        String[] allowedMappings = loadPropertyValue(connectionUser.getName(), connectionUser.getRealm());
        if (allowedMappings.length == 1 && "*".equals(allowedMappings[1])) {
            // A wild card mapping was found.
            return true;
        }
        for (String current : allowedMappings) {
            if (requestedUser.equals(current)) {
                return true;
            }
        }
        return false;
    }
    
Voir le fichier Quickstart ejb-security-interceptors README pour obtenir des instructions complètes et des informations détaillées sur le code.

7.6.5. Sécurité supplémentaire pour l'authentification EJB

Résumé

Par défaut, lorsque vous effectuez un appel distant à un EJB déployé sur le serveur d'applications, la connexion au serveur est authentifiée et toute demande reçue via cette connexion est exécutée en utilisant les informations d'identification qui ont authentifié la connexion. L'authentification au niveau de la connexion dépend des capacités des mécanismes sous-jacents SASL (Simple Authentication and Security Layer). Plutôt que d'écrire des mécanismes SASL personnalisés, vous pouvez ouvrir et authentifier une connexion au serveur, puis ajouter les jetons de sécurité supplémentaires avant d'appeler un EJB. Cette rubrique décrit comment passer des informations supplémentaires sur la connexion existante du client pour l'authentification de l'EJB.

Les exemples de code ci-dessous sont uniquement à des fins de démonstration. Ils ne présentent qu'une approche possible et doivent être personnalisés pour répondre aux besoins précis de l'application. Le mot de passe est échangé par le mécanisme SASL. Si l'authentification DIGEST-MD5 SASL est utilisée, le mot de passe est toujours haché avec difficulté et non clair. Toutefois, les jetons restants, eux, sont clairs. Si ces jetons contiennent des informations sensibles, vous pouvez activer le cryptage pour la connexion.

Procédure 7.13. Information de sécurité pour l'authentification EJB

Pour un token de sécurité supplémentaire de connexion authentifiée, vous devrez créer les 3 composants suivants.
  1. Créer l'intercepteur côté client

    Cet intercepteur doit implémenter org.jboss.ejb.client.EJBClientInterceptor. L'intercepteur doit passer le token de sécurité supplémentaire par le mappage de données contextuelles, par l'intermédiaire d'un appel EJBClientInvocationContext.getContextData(). Voici un exemple de code d'intercepteur côté client qui crée un token de sécurité supplémentaire :
    public class ClientSecurityInterceptor implements EJBClientInterceptor {
    
        public void handleInvocation(EJBClientInvocationContext context) throws Exception {
            Principal currentPrincipal = SecurityActions.securityContextGetPrincipal();
    
            if (currentPrincipal != null) {
                Map<String, Object> contextData = context.getContextData();
                contextData.put(ServerSecurityInterceptor.DELEGATED_USER_KEY, currentPrincipal.getName());
            }
    
            context.sendRequest();
        }
    
        public Object handleInvocationResult(EJBClientInvocationContext context) throws Exception {
            return context.getResult();
        }
    
    }
    
    Pour obtenir des informations sur la façon de connecter l'intercepteur client dans une application, voir Section 7.6.6, « Utiliser un intercepteur côté client dans une application ».
  2. Créer et configurer l'intercepteur du conteneur côté serveur

    Les classes d'intercepteur de conteneur sont de simples Plain Old Java Objects (POJOs). Elles utilisent @javax.annotation.AroundInvoke pour marquer la méthode qui est invoquée lors de l'invocation sur le bean. Pour plus d'informations sur les intercepteurs de conteneur, consulter : Section 7.6.1, « Intercepteurs de conteneurs ».
    1. Créer l'intercepteur de conteneur

      Cet intercepteur récupère le jeton d'authentification de sécurité du contexte et le passe au domaine JAAS (Java Authentication and Autorisation Service) pour vérification. Voici un exemple de code d'intercepteur de conteneur :
      public class ServerSecurityInterceptor {
      
          private static final Logger logger = Logger.getLogger(ServerSecurityInterceptor.class);
      
          static final String DELEGATED_USER_KEY = ServerSecurityInterceptor.class.getName() + ".DelegationUser";
      
          @AroundInvoke
          public Object aroundInvoke(final InvocationContext invocationContext) throws Exception {
              Principal desiredUser = null;
              UserPrincipal connectionUser = null;
      
              Map<String, Object> contextData = invocationContext.getContextData();
              if (contextData.containsKey(DELEGATED_USER_KEY)) {
                  desiredUser = new SimplePrincipal((String) contextData.get(DELEGATED_USER_KEY));
      
                  Collection<Principal> connectionPrincipals = SecurityActions.getConnectionPrincipals();
      
                  if (connectionPrincipals != null) {
                      for (Principal current : connectionPrincipals) {
                          if (current instanceof UserPrincipal) {
                              connectionUser = (UserPrincipal) current;
                              break;
                          }
                      }
      
                  } else {
                      throw new IllegalStateException("Delegation user requested but no user on connection found.");
                  }
              }
      
      
              ContextStateCache stateCache = null;
              try {
                  if (desiredUser != null && connectionUser != null
                      && (desiredUser.getName().equals(connectionUser.getName()) == false)) {
                      // The final part of this check is to verify that the change does actually indicate a change in user.
                      try {
                          // We have been requested to use an authentication token
                          // so now we attempt the switch.
                          stateCache = SecurityActions.pushIdentity(desiredUser, new OuterUserCredential(connectionUser));
                      } catch (Exception e) {
                          logger.error("Failed to switch security context for user", e);
                          // Don't propagate the exception stacktrace back to the client for security reasons
                          throw new EJBAccessException("Unable to attempt switching of user.");
                      }
                  }
      
                  return invocationContext.proceed();
              } finally {
                  // switch back to original context
                  if (stateCache != null) {
                      SecurityActions.popIdentity(stateCache);;
                  }
              }
          }
      
      
    2. Configurer l'intercepteur du conteneur

      Pour plus d'informations sur la façon de configurer les intercepteurs de conteneurs côté serveur, consulter : Section 7.6.3, « Configurer un intercepteur de conteneur ».
  3. Créer le JAAS LoginModule

    Ce module personnalisé exécute l'authentification à l'aide de l'information de la connexion authentifiée existante ainsi qu'à l'aide qu'un jeton de sécurité supplémentaire. Voici un exemple de code réduit qui utilise le jeton de sécurité supplémentaire et qui exécute l'authentification. On peut trouver l'exemple de code complet dans le quickstart ejb-security-interceptors fourni dans JBoss EAP 6.3 ou version supérieure.
    public class DelegationLoginModule extends AbstractServerLoginModule {
    
        private static final String DELEGATION_PROPERTIES = "delegationProperties";
    
        private static final String DEFAULT_DELEGATION_PROPERTIES = "delegation-mapping.properties";
    
        private Properties delegationMappings;
    
        private Principal identity;
    
        @Override
        public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
            addValidOptions(new String[] { DELEGATION_PROPERTIES });
            super.initialize(subject, callbackHandler, sharedState, options);
    
            String propertiesName;
            if (options.containsKey(DELEGATION_PROPERTIES)) {
                propertiesName = (String) options.get(DELEGATION_PROPERTIES);
            } else {
                propertiesName = DEFAULT_DELEGATION_PROPERTIES;
            }
            try {
                delegationMappings = loadProperties(propertiesName);
            } catch (IOException e) {
                throw new IllegalArgumentException(String.format("Unable to load properties '%s'", propertiesName), e);
            }
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public boolean login() throws LoginException {
            if (super.login() == true) {
                log.debug("super.login()==true");
                return true;
            }
    
            // Time to see if this is a delegation request.
            NameCallback ncb = new NameCallback("Username:");
            ObjectCallback ocb = new ObjectCallback("Password:");
    
            try {
                callbackHandler.handle(new Callback[] { ncb, ocb });
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                }
                return false; // If the CallbackHandler can not handle the required callbacks then no chance.
            }
    
            String name = ncb.getName();
            Object credential = ocb.getCredential();
    
            if (credential instanceof OuterUserCredential) {
                // This credential type will only be seen for a delegation request, if not seen then the request is not for us.
    
                if (delegationAcceptable(name, (OuterUserCredential) credential)) {
    
                    identity = new SimplePrincipal(name);
                    if (getUseFirstPass()) {
                        String userName = identity.getName();
                        if (log.isDebugEnabled())
                            log.debug("Storing username '" + userName + "' and empty password");
                        // Add the username and an empty password to the shared state map
                        sharedState.put("javax.security.auth.login.name", identity);
                        sharedState.put("javax.security.auth.login.password", "");
                    }
                    loginOk = true;
                    return true;
                }
            }
    
            return false; // Attempted login but not successful.
        }
    
    
    
  4. Ajouter le LoginModule personnalisé à la chaîne

    Vous devez ajouter le nouveau LoginModule personnalisé à l'endroit qui convient dans la chaîne pour qu'il soit invoqué dans le bon ordre. Dans cet exemple, le SaslPlusLoginModule doit être mis dans la chaîne avant le LoginModule qui charge les rôles par l'option password-stacking .
    • Configurer l'ordonnancement du LoginModule par le Management CLI

      Ce qui suit est un exemple de commandes de Management CLI qui enchaînent le SaslPlusLoginModule personnalisé avant que le LoginModule RealmDirect définisse l'option password-stacking.
      /subsystem=security/security-domain=quickstart-domain:add(cache-type=default)
      /subsystem=security/security-domain=quickstart-domain/authentication=classic:add
      /subsystem=security/security-domain=quickstart-domain/authentication=classic/login-module=DelegationLoginModule:add(code=org.jboss.as.quickstarts.ejb_security_plus.SaslPlusLoginModule,flag=optional,module-options={password-stacking=useFirstPass})
      /subsystem=security/security-domain=quickstart-domain/authentication=classic/login-module=RealmDirect:add(code=RealmDirect,flag=required,module-options={password-stacking=useFirstPass})
      Pour plus d'informations sur le Management CLI, voir le chapitre intitulé Management Interfaces du guide Administration and Configuration Guide de JBoss EAP 6 qui se trouve sur le portail clients https://access.redhat.com/site/documentation/JBoss_Enterprise_Application_Platform/
    • Configurer l'ordonnancement du LoginModule manuellement

      Ce qui suit est un exemple de XML qui configure l'ordonnancement du LoginModule dans le sous-système de security du fichier de configuration du serveur. Le SaslPlusLoginModule doit précéder le LoginModule RealmDirect pour qu'il puisse vérifier l'utilisateur distant avant que les rôles utilisateurs ne soient chargés et l'option password-stacking définie.
      <security-domain name="quickstart-domain" cache-type="default">
          <authentication>
              <login-module code="org.jboss.as.quickstarts.ejb_security_plus.SaslPlusLoginModule" flag="required">
                  <module-option name="password-stacking" value="useFirstPass"/>
              </login-module>
              <login-module code="RealmDirect" flag="required">
                  <module-option name="password-stacking" value="useFirstPass"/>
              </login-module>
          </authentication>
      </security-domain>
      
  5. Créer le client distant

    Dans l'exemple de code suivant, on assume que le fichier additional-secret.properties auquel accède le JAAS LoginModule ci-dessus contient la propriété suivante :
    quickstartUser=7f5cc521-5061-4a5b-b814-bdc37f021acc
    
    Le code suivant montre comment créer un token de sécurité et comment le définir avant l'appel EJB. Le token secret est codé en dur dans des buts de démonstration uniquement. Ce client se contente d'afficher les résultats sur la console.
    import static org.jboss.as.quickstarts.ejb_security_plus.EJBUtil.lookupSecuredEJB;
    
    public class RemoteClient {
    
        /**
         * @param args
         */
        public static void main(String[] args) throws Exception {
            SimplePrincipal principal = new SimplePrincipal("quickstartUser");
            Object credential = new PasswordPlusCredential("quickstartPwd1!".toCharArray(), "7f5cc521-5061-4a5b-b814-bdc37f021acc");
    
            SecurityActions.securityContextSetPrincipalCredential(principal, credential);
            SecuredEJBRemote secured = lookupSecuredEJB();
    
            System.out.println(secured.getPrincipalInformation());
        }
    }
    

7.6.6. Utiliser un intercepteur côté client dans une application

Résumé

Vous pouvez connecter un intercepteur côté client dans une application par programmation ou en utilisant un mécanisme ServiceLoader. La procédure suivante décrit les deux méthodes.

Procédure 7.14. Connecter l'intercepteur

    • Par programmation

      Par cette approche, vous appelez l'API org.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor) et passez l'instance order et l'instance interceptor. L'instance order est utilisée pour déterminer où exactement cet interceptor est placé dans la chaîne d'intercepteur du client.
    • Mécanisme ServiceLoader

      Cette approche nécessite la création d'un fichier META-INF/services/org.jboss.ejb.client.EJBClientInterceptor et le placer ou le mettre dans le chemin de classe de l'application client. Les règles du fichier sont dictées par le Java ServiceLoader Mechanism. Ce fichier devrait contenir, dans chaque ligne distincte, le nom de classe qualifié complet de l'implémentation d'intercepteur de client EJB. Les classes d'intercepteur de client EJB doivent être disponibles dans le chemin de classe. Les intercepteurs de client EJB ajoutés en utilisant le mécanisme de ServiceLoader sont ajoutés à la fin de la chaîne d'intercepteur de client, dans l'ordre dans lequel ils se trouvent dans le chemin de classe (classpath). Le démarrage rapide ou quickstart ejb-security-interceptors utilise cette approche.

7.7. JavaBeans Enterprise clusterisés

7.7.1. JavaBeans clusterisées (EJB)

Les composants EJB peuvent être clusterisés dans les scénarios de haute disponibilité. Ils utilisent différents protocoles HTTP, donc ils doivent être clusterisés différemment. Les beans avec ou sans état EJB 2 et 3 peuvent être clusterisés.
Pour plus d'informations sur les singletons, veuillez consulter : Section 9.4, « Implémenter un Singleton HA ».

Note

Les bean d'entité EJB 2 ne peuvent pas être clusterisés dans EAP 6 etc. C'est un problème de migration.

7.7.2. Configuration de client EJB In-server ou Autonome

Pour connecter un client EJB à une application EJB clusterisée, vous devez étendre la configuration existante dans le client EJB autonome ou dans l'EJB In-server afin d'inclure la configuration de connexion de cluster. Le fichier jboss-ejb-client.properties pour un client EJB autonome, ou même le fichier jboss-ejb-client.xml pour une application côté serveur doit être étendue pour inclure une configuration de cluster.
L'exemple suivant montre la configuration de connexion d'un client EJB autonome. L'exemple suivant ne montre que la configuration de cluster supplémentaire. L'exemple suivant (fichier de configuration jboss-ejb-client.xml) montre la configuration complète :

Exemple 7.3. Client autonome avec configuration jboss-ejb-client.properties

remote.clusters=ejb
remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED=false
remote.cluster.ejb.username=test
remote.cluster.ejb.password=password
Si une application utilise une remote-outbound-connection, vous devez configurer le fichier jboss-ejb-client.xml et ajouter la configuration de cluster comme illustré dans l'exemple suivant :

Exemple 7.4. Application client déployée dans une autre instance EAP 6 (Configurer le fichier jboss-ejb-client.xml)

<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
   <client-context>
      <ejb-receivers>
            <!-- this is the connection to access the app-one -->
        <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-1" />
            <!-- this is the connection to access the app-two -->
        <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-2" />
      </ejb-receivers>
        
<!-- if an outbound connection connects to a cluster; a list of members is provided after successful connection.
To connect to this node this cluster element must be defined. -->
        
    <clusters>
       <!-- cluster of remote-ejb-connection-1 -->
       <cluster name="ejb" security-realm="ejb-security-realm-1" username="quickuser1">
          <connection-creation-options>
            <property name="org.xnio.Options.SSL_ENABLED" value="false" />
            <property name="org.xnio.Options.SASL_POLICY_NOANONYMOUS" value="false" />
          </connection-creation-options>
       </cluster>
    </clusters>
   </client-context>
</jboss-ejb-client>

Note

Pour avoir une connexion sécurisée, vous devez ajouter les identifiants à la configuration du cluster pour éviter une exception d'authentification.

7.7.3. Mettre en place une politique d'équilibrage des charges presonnalisée pour les appels EJB

Il est possible de mettre en place une politique d'équilibrage des charges personnalisée/alternative de façon à ce que les serveurs de l'application n'aient pas à gérer le même nombre d'appels EJB en général ou pour une période donnée.
Vous pouvez implémenter AllClusterNodeSelector pour les appels EJB. Le comportement de sélection de noeud de AllClusterNodeSelector ressemble à celui du sélecteur par défaut, sauf que AllClusterNodeSelector utilise tous les noeuds de cluster disponibles, même pour les clusters de grande taille (nombre de noeuds>20). Si un noeud de cluster non connecté est renvoyé, il s'ouvre automatiquement. L'exemple suivant montre une implémentation de AllClusterNodeSelector :
package org.jboss.as.quickstarts.ejb.clients.selector;

import java.util.Arrays;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.ejb.client.ClusterNodeSelector;
public class AllClusterNodeSelector implements ClusterNodeSelector {
  private static final Logger LOGGER = Logger.getLogger(AllClusterNodeSelector.class.getName());
  
  @Override
  public String selectNode(final String clusterName, final String[] connectedNodes, final String[] availableNodes) {
    if(LOGGER.isLoggable(Level.FINER)) {
      LOGGER.finer("INSTANCE "+this+ " : cluster:"+clusterName+" connected:"+Arrays.deepToString(connectedNodes)+" available:"+Arrays.deepToString(availableNodes));
    }
    
    if (availableNodes.length == 1) {
        return availableNodes[0];
    }
    final Random random = new Random();
    final int randomSelection = random.nextInt(availableNodes.length);
    return availableNodes[randomSelection];
  }

}
Vous pouvez également implémenter le SimpleLoadFactorNodeSelector pour les appels EJB. L'équilibrage des charges dans SimpleLoadFactorNodeSelector a lieu sur la base d'un facteur de charge. Le facteur de charge (2/3/4) est calculé sur la base des noms de noeuds (A/B/C) irrespectivement de la charge qui pèse sur chaque noeud. L'exemple suivant vous montre une implémentation de SimpleLoadFactorNodeSelector :
package org.jboss.as.quickstarts.ejb.clients.selector;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.ejb.client.DeploymentNodeSelector;
public class SimpleLoadFactorNodeSelector implements DeploymentNodeSelector {
  private static final Logger LOGGER = Logger.getLogger(SimpleLoadFactorNodeSelector.class.getName());
  private final Map<String, List<String>[]> nodes = new HashMap<String, List<String>[]>();
  private final Map<String, Integer> cursor = new HashMap<String, Integer>();
  
  private ArrayList<String> calculateNodes(Collection<String> eligibleNodes) {
    ArrayList<String> nodeList = new ArrayList<String>();
    
    for (String string : eligibleNodes) {
      if(string.contains("A") || string.contains("2")) {
        nodeList.add(string);
        nodeList.add(string);
      } else if(string.contains("B") || string.contains("3")) {
        nodeList.add(string);
        nodeList.add(string);
        nodeList.add(string);
      } else if(string.contains("C") || string.contains("4")) {
        nodeList.add(string);
        nodeList.add(string);
        nodeList.add(string);
        nodeList.add(string);
      }
    }
    return nodeList;
  }
  
  @SuppressWarnings("unchecked")
  private void checkNodeNames(String[] eligibleNodes, String key) {
    if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) {
      // must be synchronized as the client might call it concurrent
      synchronized (nodes) {
        if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) {
          ArrayList<String> nodeList = new ArrayList<String>();
          nodeList.addAll(Arrays.asList(eligibleNodes));
          
          nodes.put(key, new List[] { nodeList, calculateNodes(nodeList) });
        }
      }
    }
  }
   private synchronized String nextNode(String key) {
    Integer c = cursor.get(key);
    List<String> nodeList = nodes.get(key)[1];
    
    if(c == null || c >= nodeList.size()) {
      c = Integer.valueOf(0);
    }
    
    String node = nodeList.get(c);
    cursor.put(key, Integer.valueOf(c + 1));
    
    return node;
  }
  
  @Override
  public String selectNode(String[] eligibleNodes, String appName, String moduleName, String distinctName) {
    if (LOGGER.isLoggable(Level.FINER)) {
      LOGGER.finer("INSTANCE " + this + " : nodes:" + Arrays.deepToString(eligibleNodes) + " appName:" + appName + " moduleName:" + moduleName
          + " distinctName:" + distinctName);
    }

    // if there is only one there is no sense to choice
    if (eligibleNodes.length == 1) {
      return eligibleNodes[0];
    }
    final String key = appName + "|" + moduleName + "|" + distinctName;
    
    checkNodeNames(eligibleNodes, key);
    return nextNode(key);
  }
}
Configuration avec jboss-ejb-client.properties

Vous devrez ajouter la propriété remote.cluster.ejb.clusternode.selector au nom de votre classe d'implémentation (AllClusterNodeSelector ou SimpleLoadFactorNodeSelector). Le sélecteur vérifiera les serveurs configurés disponibles au moment de l'invocation. l'exemple suivant utilise AllClusterNodeSelector comme sélecteur de noeud de déploiement :

remote.clusters=ejb
remote.cluster.ejb.clusternode.selector=org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector
remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED=false
remote.cluster.ejb.username=test
remote.cluster.ejb.password=password

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=one,two
remote.connection.one.host=localhost
remote.connection.one.port = 4447
remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.one.username=user
remote.connection.one.password=user123
remote.connection.two.host=localhost
remote.connection.two.port = 4547
remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false

Utiliser l'API JBoss ejb-client

Vous devrez ajouter la propriété remote.cluster.ejb.clusternode.selector à la liste de constructeur PropertiesBasedEJBClientConfiguration. L'exemple suivant utilise AllClusterNodeSelector comme sélecteur de noeud de déploiement :

Properties p = new Properties();
p.put("remote.clusters", "ejb");
p.put("remote.cluster.ejb.clusternode.selector", "org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector");
p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.cluster.ejb.username", "test");
p.put("remote.cluster.ejb.password", "password");

p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.connections", "one,two");
p.put("remote.connection.one.port", "4447");
p.put("remote.connection.one.host", "localhost");
p.put("remote.connection.two.port", "4547");
p.put("remote.connection.two.host", "localhost");

EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);
ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
EJBClientContext.setSelector(selector);

p = new Properties();
p.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext context = new InitialContext(p);

Configuration côté application de serveur avec jboss-ejb-client.xml

Pour pouvoir utiliser la politique d'équilibrage des charges pour la communication d'un serveur à un autre, empaquetez la classe avec l'application et configurez-là dans les paramètres de jboss-ejb-client.xml (qui se trouvent dans le dossier META-INF). L'exemple suivant utilise AllClusterNodeSelector comme sélecteur de noeud de déploiement :

<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
  <client-context>
    <ejb-receivers>
      <!-- this is the connection to access the app -->
      <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-1" />
    </ejb-receivers>
        
  <!-- if an outbound connection connect to a cluster a list of members is provided after successful connection.
To connect to this node this cluster element must be defined.
-->
   <clusters>
     <!-- cluster of remote-ejb-connection-1 -->
     <cluster name="ejb" security-realm="ejb-security-realm-1" username="test" cluster-node-selector="org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector">
        <connection-creation-options>
           <property name="org.xnio.Options.SSL_ENABLED" value="false" />
           <property name="org.xnio.Options.SASL_POLICY_NOANONYMOUS" value="false" />
        </connection-creation-options>
      </cluster>
   </clusters>
  </client-context>
</jboss-ejb-client>
Pour pouvoir utiliser la configuration ci-dessus en toute sécurité, il vous faudra ajouter ejb-security-realm-1 à la configuration côté serveur. L'exemple suivant vous montre les commandes CLI pour ajouter un domaine de sécurité (ejb-security-realm-1). La valeur correspond au mot de passe base64 codifié pour l'utilisateur "test" :

core-service=management/security-realm=ejb-security-realm-1:add()
core-service=management/security-realm=ejb-security-realm-1/server-identity=secret:add(value=cXVpY2sxMjMr)

Note

Si vous êtes en mode autonome, utiliser l'option de démarrage -Djboss.node.name= ou le fichier de configuration du serveur standalone.xml pour configurer le nom du serveur (server name=""). Veillez à ce que le nom de serveur soit unique. En mode de domaine, le contrôleur valide automatiquement le fait que les noms soient uniques.

7.7.4. Comportement de transaction des invocations EJB

Invocations de serveur à serveur

Les attributs de transaction d'applications distribuées EAP doivent être gérés comme si l'application était appelée sur le même serveur. Pour mettre fin à une transaction, la méthode de destination doit comporter la mention REQUIRES_NEW, à l'aide d'interfaces différentes.

Note

EAP 6 n'a pas besoin de JTS ( de l'anglais Java Transaction Services) pour la propagation de la transaction sur les invocations EJB d'un serveur à l'autre si les deux serveurs sont EAP 6. La bibliothèque d'API cliente de JBoss EJB la gère elle-même.
Les invocations côté client

Pour invoquer les beans de session EJB avec un client EAP 6 autonome, le client doit avoir une référence à l'objet InitialContext tandis que les proxies EJB ou UserTransaction sont utilisés. Il est également important de garder l'objet InitialContext ouvert tant que les proxies EJB ou UserTransaction sont utilisés. Le contrôle des connexions se fera à l'intérieur des classes créées par InitialContext avec les propriétés.

L'exemple suivant vous montre l'API du client EJB qui contient la référence à l'objet InitialContext :
package org.jboss.as.quickstarts.ejb.multi.server;

import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.jboss.as.quickstarts.ejb.multi.server.app.MainApp;
import org.jboss.ejb.client.ContextSelector;
import org.jboss.ejb.client.EJBClientConfiguration;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration;
import org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector;

public class Client {

/**
* @param args no args needed
* @throws Exception
*/
    public static void main(String[] args) throws Exception {
        // suppress output of client messages
        Logger.getLogger("org.jboss").setLevel(Level.OFF);
        Logger.getLogger("org.xnio").setLevel(Level.OFF);
        
        Properties p = new Properties();
        p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
        p.put("remote.connections", "one");
        p.put("remote.connection.one.port", "4447");
        p.put("remote.connection.one.host", "localhost");
        p.put("remote.connection.one.username", "quickuser");
        p.put("remote.connection.one.password", "quick-123");

        EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);
        ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
        EJBClientContext.setSelector(selector);

        Properties props = new Properties();
        props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        InitialContext context = new InitialContext(props);

        
        final String rcal = "ejb:jboss-ejb-multi-server-app-main/ejb//" + ("MainAppBean") + "!" + MainApp.class.getName();
        final MainApp remote = (MainApp) context.lookup(rcal);
        final String result = remote.invokeAll("Client call at "+new Date());

        System.out.println("InvokeAll succeed: "+result);
    }

}

Note

L'obtention d'une réféence UserTransaction sur un client n'est pas prise en charge pour les scénarios de contextes client EJB scoped et pour les invocations qui utilisent le protocole remote-naming. C'est parce que dans ces scénarios, InitialContext encapsule sa propre instance de contexte client, à laquelle on ne peut pas accéder par les méthodes statiques de la classe EJBClient. Quand la méthode EJBClient.getUserTransaction() est invoquée, elle renvoie une transaction en provenance du contexte client EJB (global) par défaut (pas forcément initialisé) et non pas en provenance du contexte souhaité.
Référence UserTransaction côté client

L'exemple suivant vous montre comment obtenir une référence UserTransaction sur un client autonome :

import org.jboss.ejb.client.EJBClient;
import javax.transaction.UserTransaction;
.
.
    Context context=null;
    UserTransaction tx=null;
    try {
      Properties props = new Properties();
      // REMEMBER: there must be a jboss-ejb-client.properties with the connection parameter
      //           in the clients classpath
      props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
      context = new InitialContext(props);  
      System.out.println("\n\tGot initial Context: "+context);
      tx=EJBClient.getUserTransaction("yourServerName");
      System.out.println("UserTransaction = "+tx.getStatus());
      tx.begin();
      // do some work
      ...
    }catch (Exception e) {
      e.printStackTrace();
      tx.rollback();
    }finally{
      if(context != null) {
        context.close();
      }
    }

Note

Pour obtenir une référence UserTransaction côté client, démarrer votre serveur avec la propriété système suivante -Djboss.node.name=yourServerName et utilisez-là ensuite côté client comme suit :
tx=EJBClient.getUserTransaction("yourServerName");
Remplacer "yourServerName" par le nom de votre serveur. Si une transaction utilisateur démarre sur un nœud, toutes les invocations seront sticky sur le nœud et le nœud devra avoir tous les EJB nécessaires. Il n'est pas possible d'utiliser UserTransaction avec un protocole de nommage à distance ou avec un scoped-context.

7.8. Référence

7.8.1. Référence de nommage EJB JNDI

Le nom de recherche JNDI d'une session bean a la syntaxe suivante :
 ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>?stateful 
<appName>
Si le fichier JAR du session bean a été déployé dans un EAR, alors ce se sera le nom de l'EAR. Par défaut, le nom d'un EAR correspond à son nom de fichier sans le suffixe .ear. Le nom de l'application peut également être remplacé dans son fichier application.xml. Si le bean de session n'est pas déployé dans un EAR, laissez le vide.
<moduleName>
Le nom du module correspond au nom du fichier dans lequel le session bean est déployé. Par défaut, le nom du fichier JAR correspond à son nom de fichier sans le suffixe .jar. Le nom du module peut également être remplacé par le ejb-jar.xml du JAR.
<distinctName>
JBoss EAP 6 permet à chaque déploiement de spécifier un nom distinct en option. Si le déploiement n'a pas de nom distinct, laisser le vide.
<beanName>
Le nom du bean est le nom de classe de la session bean à invoquer.
<viewClassName>
Le nom de classe de vue est le nom de classe complet de l'interface distante de l'interface distante. Inclut le nom du package de l'interface.
?stateful
Le suffixe ?stateful est requis quand le nom JNDI se réfère à un bean de session stateful. Non inclus dans d'autres types de bean.

7.8.2. Résolution de référence EJB

Cette section explique comment JBoss implémente @EJB et @Resource. Veuillez noter qu'XML remplace toujours les annotations, mais que les mêmes règles s'appliquent.

Règles pour les annotations @EJB

  • L'annotation @EJB possède également un attribut mappedName(). La spécification le considère comme une métadonnée spécifique au fournisseur tandis que JBoss reconnait mappedName() en tant que nom JNDI global du EJB que vous référencez. Si vous avez indiqué un mappedName(), alors tous les autres attributs seront ignorés et ce nom JNDI global sera utilisé comme liaison.
  • Si vous indiquez @EJB sans attributs définis :
    @EJB 
    ProcessPayment myEjbref;
    Alors les règles suivantes vont s'appliquer :
    • Le jar EJB du bean de référencement est analysé pour trouver un EJB avec l'interface utilisée dans l'injection @EJB. S'il existe plus d'un EJB qui publie la même interface commerciale, alors une exception apparaîtra. S'il n'y a qu'un seul bean dans cette interface, alors celui-là sera utilisé.
    • Chercher l'EAR des EJB qui publient cette interface. S'il y a des doubles, alors il y aura une exception. Sinon le bean correspondant sera renvoyé.
    • Chercher globalement dans le runtime de JBoss un EJB de cette interface. Une fois de plus, s'il y a des doubles, il y aura une exception.
  • @EJB.beanName() correspond à <ejb-link>. Si le beanName() est défini, il vous faudra utiliser le même algorithme que @EJB sans attribut défini mis à part beanName() comme une clé de la recherche. Il existe une exception à cette règle si vous utilisez la syntaxe ejb-link '#'. La syntaxe '#' vous permettra de mettre un chemin d'accès relatif dans un jar de l'EJB qui contient l'EJB de référencement. Reportez-vous à la spécification EJB 3.1 pour plus de détails.

7.8.3. Dépendances de projet pour les clients EJB distants

Les projets Maven qui incluent l'invocation de session beans de clients éloignés ont besoin des dépendances suivantes du référentiel JBoss EAP 6 Maven.

Tableau 7.4. Dépendances Maven pour les clients EJB

GroupID ArtifactID
org.jboss.spec jboss-javaee-6.0
org.jboss.as jboss-as-ejb-client-bom
org.jboss.spec.javax.transaction jboss-transaction-api_1.1_spec
org.jboss.spec.javax.ejb jboss-ejb-api_3.1_spec
org.jboss jboss-ejb-client
org.jboss.xnio xnio-api
org.jboss.xnio xnio-nio
org.jboss.remoting3 jboss-remoting
org.jboss.sasl jboss-sasl
org.jboss.marshalling jboss-marshalling-river
A l'exception de jboss-javaee-6.0 et jboss-as-ejb-client-bom, ces dépendances doivent être ajoutées à la section <dependencies> du fichier pom.xml.
Les dépendances jboss-javaee-6.0 et jboss-as-ejb-client-bom doivent être ajoutées à la section <dependencyManagement> du fichier pom.xml avec le scope d' import.

Note

Les versions de artifactID peuvent être modifiées. Veuillez consulter le référentiel Maven pour connaître la version la plus récente.
<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>org.jboss.spec</groupId>
         <artifactId>jboss-javaee-6.0</artifactId>
         <version>3.0.0.Final-redhat-1</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>

      <dependency>
         <groupId>org.jboss.as</groupId>
         <artifactId>jboss-as-ejb-client-bom</artifactId>
         <version>7.1.1.Final-redhat-1</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>
Voir ejb-remote/client/pom.xml dans les fichiers quickstart pour obtenir un exemple complet de configuration de dépendance pour une invocation de session bean éloignée.

7.8.4. Référence de descripteur de déploiement jboss-ejb3.xml

jboss-ejb3.xml est un descripteur de déploiement personnalisé pouvant être utilisé dans un JAR EJB ou des archives WAR. Dans une archive JAR EJB, il doit être situé dans le répertoire META-INF/. Dans une archive WAR, il doit être situé dans le répertoire WEB-INF/.
Le format ressemble à ejb-jar.xml, qui utilise les mêmes espace-noms et qui fournit des espace-noms supplémentaires. Les contenus de jboss-ejb3.xml sont mergés avec les contenus de ejb-jar.xml, et les items jboss-ejb3.xml ont la priorité.
Ce document ne couvre que les espace-noms supplémentaires non-standard utilisés par jboss-ejb3.xml. Voir http://java.sun.com/xml/ns/javaee/ pour la documentation sur les espace-noms standard.
L'espace-nom root est http://www.jboss.com/xml/ns/javaee.

Espace-nom de descripteur d'assemblage

Les espace-noms suivants peuvent être utilisés dans l'élément <assembly-descriptor>. Ils peuvent être utilisés pour appliquer leur configuration à un simple bean, ou à tous les beans du déploiement en utilisant \* comme ejb-name.
Espace-nom de clustering : urn:clustering:1.0
xmlns:c="urn:clustering:1.0"
Cela vous permet de marquer les EJB comme étant clusterisés. Il s'agit du descripteur de déploiement équivalent au @org.jboss.ejb3.annotation.Clustered.
<c:clustering>
   <ejb-name>DDBasedClusteredSFSB</ejb-name>
   <c:clustered>true</c:clustered>
</c:clustering>
L'espace-nom de sécurité (urn:security)
xmlns:s="urn:security"
Cela vous permet de définir le domaine de sécurité et le principal prétendant pour l'EJB.
<s:security>
  <ejb-name>*</ejb-name>
  <s:security-domain>myDomain</s:security-domain>
  <s:run-as-principal>myPrincipal</s:run-as-principal>
</s:security>
L'espace-nom d'adaptateur de ressource : urn:resource-adapter-binding
xmlns:r="urn:resource-adapter-binding"
Cela vous permet de définir l'adaptateur de ressource d'un Message-Driven Bean.
<r:resource-adapter-binding>
  <ejb-name>*</ejb-name>
  <r:resource-adapter-name>myResourceAdapter</r:resource-adapter-name>
</r:resource-adapter-binding>
L'espace-nom IIOP : urn:iiop
xmlns:u="urn:iiop"
L'espace-nom IIOP est l'endroit où les paramètres IIOP sont configurés.
L'espace-nom du pool : urn:ejb-pool:1.0
xmlns:p="urn:ejb-pool:1.0"
Cela vous permet de sélectionner le pool qui est utilisé par les stateless session beans ou par les Message-Driven Beans. Les pools sont définis dans la configuration du serveur.
<p:pool>
   <ejb-name>*</ejb-name>
   <p:bean-instance-pool-ref>my-pool</p:bean-instance-pool-ref>
</p:pool>
L'espace-nom du cache : urn:ejb-cache:1.0
xmlns:c="urn:ejb-cache:1.0"
Cela vous permet de sélectionner le cache qui est utilisé par les stateful session beans inclus. Les caches sont définis dans la configuration du serveur.
<c:cache>
   <ejb-name>*</ejb-name>
   <c:cache-ref>my-cache</c:cache-ref>
</c:cache>

Exemple 7.5. Fichier exemple de jboss-ejb3.xml

<?xml version="1.1" encoding="UTF-8"?>
   <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
                  xmlns="http://java.sun.com/xml/ns/javaee"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:c="urn:clustering:1.0"
                  xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
                  version="3.1"
                  impl-version="2.0">
      <enterprise-beans>
         <message-driven>
            <ejb-name>ReplyingMDB</ejb-name>
            <ejb-class>org.jboss.as.test.integration.ejb.mdb.messagedestination.ReplyingMDB</ejb-class>
            <activation-config>
               <activation-config-property>
                  <activation-config-property-name>destination</activation-config-property-name>
                  <activation-config-property-value>java:jboss/mdbtest/messageDestinationQueue
                  </activation-config-property-value>
               </activation-config-property>
            </activation-config>
         </message-driven>
      </enterprise-beans>
      <assembly-descriptor>
         <c:clustering>
            <ejb-name>DDBasedClusteredSFSB</ejb-name>
            <c:clustered>true</c:clustered>
         </c:clustering>
      </assembly-descriptor>
   </jboss:ejb-jar>

Chapitre 8. Les services JBoss MBean

8.1. Rédiger des services JBoss MBean

Rédiger un service MBean personnalisé qui s'appuie sur un service JBoss nécessite un modèle de méthode d'interface de service. Le modèle de méthode d'interface de service de JBoss MBean se compose d'un ensemble d'opérations de cycle de vie qui informent un service MBean quand il se crée, quand il démarre, s'arrête ou se détruit lui-même.
Vous pouvez gérer l'état de la dépendance par une des approches suivantes :
  • Si vous souhaitez que des méthodes spécifiques soient invoquées sur votre MBean, il vous faudra déclarer ces méthodes dans votre interface MBean. Cette approche permet à votre implementation MBean d'éviter des dépendances sur les classes JBoss.
  • Si vous ne vous inquiétez pas des dépendances sur les classes spécifiques à JBoss, alors vous souhaiterez sans doute que votre interface MBean prolonge l'interface ServiceMBean et la classe ServiceMBeanSupport. La classe ServiceMBeanSupport fournit des implémentations des méthodes de cycle de vie de services comme create, start et stop. Pour un événement spécifique comme start(), vous devrez surcharger la méthode startService() donnée par la classe ServiceMBeanSupport.

8.2. Exemple de MBean standard

Cette section développe deux exemples de services MBean qui font partie d'un même package d'archive de service (.sar).
L'interface ConfigServiceMBean déclare des méthodes spécifiques telles que les méthodes start, getTimeout et stop pour qu'elles démarrent, mettent en attente et stoppent le MBean correctement sans utiliser de classes spécifiques à JBoss. La classe ConfigService implémente l'interface ConfigServiceMBean et de ce fait implémente les méthodes utilisées dans cette interface.
La classe PlainThread étend la classe ServiceMBeanSupport et implémente l'interface PlainThreadMBean. PlainThread démarre un thread et utilise la méthode ConfigServiceMBean.getTimeout() pour déterminer la durée pendant laquelle le thread doit dormir.

Exemple 8.1. Exemples de services MBean

package org.jboss.example.mbean.support;

public interface ConfigServiceMBean {

    int getTimeout();

    void start();

    void stop();

}

package org.jboss.example.mbean.support;

public class ConfigService implements ConfigServiceMBean {
    int timeout;


    @Override
    public int getTimeout() {
        return timeout;
    }

    @Override
    public void start() {
        //Create a random number between 3000 and 6000 milliseconds
        timeout = (int)Math.round(Math.random() * 3000) + 3000;
        System.out.println("Random timeout set to " + timeout + " seconds");
    }

    @Override
    public void stop() {
        timeout = 0;
    }

}
  
package org.jboss.example.mbean.support;

import org.jboss.system.ServiceMBean;

public interface PlainThreadMBean extends ServiceMBean {
    void setConfigService(ConfigServiceMBean configServiceMBean);
}

package org.jboss.example.mbean.support;

import org.jboss.system.ServiceMBeanSupport;

public class PlainThread extends ServiceMBeanSupport implements PlainThreadMBean {

    private ConfigServiceMBean configService;
    private Thread thread;
    private volatile boolean done;

    @Override
    public void setConfigService(ConfigServiceMBean configService) {
        this.configService = configService;
    }

    @Override
    protected void startService() throws Exception {
        System.out.println("Starting Plain Thread MBean");
        done = false;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (!done) {
                        System.out.println("Sleeping....");
                        Thread.sleep(configService.getTimeout());
                        System.out.println("Slept!");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
        thread.start();
    }

    @Override
    protected void stopService() throws Exception {
        System.out.println("Stopping Plain Thread MBean");
        done = true;
    }


}
Le descripteur jboss-service.xml montre comment la classe ConfigService est injectée dans la classe PlainThread en utilisant la balise inject. La balise inject établit une dépendance entre PlainThreadMBean et ConfigServiceMBean permettant ainsi à PlainThreadMBean d'utiliser ConfigServiceMBean aisément.

Exemple 8.2. Décripteur de service JBoss-service.xml

<server xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:jboss:service:7.0 jboss-service_7_0.xsd"
        xmlns="urn:jboss:service:7.0">
 <mbean code="org.jboss.example.mbean.support.ConfigService" name="jboss.support:name=ConfigBean"/>
 <mbean code="org.jboss.example.mbean.support.PlainThread" name="jboss.support:name=ThreadBean">
  <attribute name="configService">
   <inject bean="jboss.support:name=ConfigBean"/>
  </attribute>
 </mbean>
</server>
Aprés avoir rédigé les exemples de MBeans, vous pouvez empaqueter les classes et le descripteur jboss-service.xml dans le dossier META-INF d'une archive de service (.sar).

8.3. Déployer JBoss MBean Services

Pour créer et déployer l'exemple de MBeans (ServiceMBeanTest.sar) en mode Domain, utliser les commandes suivantes :
[domain@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar
[domain@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar --all-server-groups
Pour créer et déployer l'exemple de MBeans (ServiceMBeanTest.sar) en mode Standalone, utliser la commande suivante :
[standalone@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar
Pour annuler le déploiement de l'exemple MBeans, utiliser la commande suivante :
[standalone@localhost:9999 /] undeploy ServiceMBeanTest.sar

Chapitre 9. Clustering dans les applications web

9.1. Réplique de session

9.1.1. La réplique de session HTTP

La réplique de session veille à ce que les sessions client d'applications distribuables ne soient pas interrompues par des défaillances de noeuds dans le système. Chaque noeud du cluster partage des informations sur les sessions en cours, et peut les récupérer si le noeud d'origine disparaît.
La réplique de session est un mécanisme par lequel les clusters mod_cluster, mod_jk, mod_proxy, ISAPI, et NSAPI procurent une haute disponibilité.

9.1.2. Cache de session web

Le cache de session web peut être configuré quand vous utilisez l'un des profils HA, dont le profil standalone-ha.xml, ou les profils de domaine gérés ha ou full-ha. Les éléments les plus couramment configurés sont le mode cache et le nombre de propriétaires de cache pour un cache distribué.
Mode Cache

Le mode cache peut être REPL (par défaut) ou DIST.

REPL
Le mode REPL reproduit tout le cache à un nœud sur deux dans le cluster. Cela constitue l'option la plus sûre, mais elle introduit aussi plus de surcharge de temps.
DIST
Le mode DIST est semblable au buddy mode fourni dans les exécutions précédentes. Il réduit la surcharge de temps en répartissant le cache entre le nombre de nœuds indiqués dans le paramètre owners. Ce nombre de propriétaires est défini par défaut à 2.
Propriétaires

Le paramètre owners contrôle combien de nœuds de clusters détiennent des duplicatas de la session. La valeur par défaut est 2.

9.1.3. Configurer le cache de session web

Par défaut, le cache de session web est REPL. Si vous souhaitez utiliser le mode DIST, exécutez les deux commandes suivantes dans 'interface CLI. Si vous utilisez un profil différent, changez le nom de profil dans les commandes. Si vous utilisez un serveur autonome, retirez la partie /profile=ha des commandes.

Procédure 9.1. Configurer le cache de session web

  1. Modifier le mode cache par défaut en DIST.

    /profile=ha/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=dist)
    
  2. Définir le nombre de propriétaires de cache distribué.

    La commande suivante définit 5 propriétaires. La valeur par défaut est 2.
    /profile=ha/subsystem=infinispan/cache-container=web/distributed-cache=dist/:write-attribute(name=owners,value=5)
    
  3. Modifier le mode cache par défaut en REPL.

    /profile=ha/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=repl)
    
  4. Relancer le serveur

    Après avoir modifié le mode cache du web, vous devez relancer le serveur.
Résultat

Votre serveur est configuré pour une copie de session. Pour utiliser une copie de session dans vos propres applications, veuillez vous référer au sujet suivant : Section 9.1.4, « Activer la copie de session pour votre application ».

9.1.4. Activer la copie de session pour votre application

Résumé

Pour profiter des fonctionnalités HA (High Availability) de JBoss EAP 6, configurer votre application de manière à ce qu'elle soit distribuable. Cette procédure vous montrera comment y parvenir puis vous expliquera certaines options de configuration avancées que vous pourrez utiliser.

Procédure 9.2. Rendez votre Application Distribuable

  1. Prérequis : indiquer que votre application est distribuable

    Si votre application n'apparaît pas comme distribuable, ses sessions ne seront jamais distribuées. Ajouter l'élément <distributable /> dans la balise <web-app> du fichier descriptif web.xml de votre application. Voici un exemple :

    Exemple 9.1. Configuration minimum pour une application distribuable

    <?xml version="1.0"?>
    <web-app  xmlns="http://java.sun.com/xml/ns/j2ee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
                                  http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" 
              version="2.4">
              
          <distributable/>
        
    </web-app>
    
    
  2. Modifier le comportement de réplication par défaut si vous le souhaitez.

    Si vous souhaitez changer une des valeurs qui affectent la réplique de session, vous pouvez les changer dans l'élément <replication-config> qui est un dépendant de l'élément enfant de l'élément <jboss-web> du fichier jboss-web.xml de votre application. Pour un exemple donné, ne l'inclure que si vous souhaitez remplacer les valeurs par défaut. L'exemple suivant énumère tous les paramètres par défaut, et est suivi d'un tableau qui explique les options les plus fréquemment modifées.

    Exemple 9.2. Valeurs <replication-config> par défaut

    <!DOCTYPE jboss-web PUBLIC
        "-//JBoss//DTD Web Application 5.0//EN"
        "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd">
    
    <jboss-web>
       
       <replication-config>
          <cache-name>custom-session-cache</cache-name>
          <replication-trigger>SET</replication-trigger>
          <replication-granularity>ATTRIBUTE</replication-granularity>
          <use-jk>false</use-jk>
          <max-unreplicated-interval>30</max-unreplicated-interval>
          <snapshot-mode>INSTANT</snapshot-mode>
          <snapshot-interval>1000</snapshot-interval>
          <session-notification-policy>com.example.CustomSessionNotificationPolicy</session-notification-policy>
      </replication-config>
    
    </jboss-web>
    
    

Tableau 9.1. Options communes pour la réplique de session

Option
Description
<replication-trigger>
Contrôle quelles conditions doivent déclencher la réplication de données de session dans l'ensemble du cluster. Cette option est utile, quand un objet mutable stocké comme attribut de session est accessible à partir de la session, le conteneur n'a aucun moyen évident de savoir si l'objet a été modifié et s'il doit être reproduit, sauf si la méthode setAttribute() est appelée directement.

Valeurs valides pour <replication-trigger>

SET_AND_GET
Il s'agit de l'option la plus sécurisée, mais à moindre performance. Les données de session sont toujours répliquées, même si leur contenu a été accédé, sans avoir été modifié. Cette configuration est préservée à but d'héritage uniquement. Éviter cette configuration et définir <max-unreplicated-interval> à 0, pour avoir le même comportement et une meilleure performance.
SET_AND_NON_PRIMITIVE_GET
La valeur par défaut. Les données de session ne sont répliquées que si on a accédé à un objet de type non primitif. Cela signifie que l'objet n'est pas d'un type Java connu comme Integer, Long, ou String.
SET
Cette option présume que l'application va explicitement appeler setAttribute sur la session quand les données auront besoin d'être répliquées. Cela empêche la réplication inutile et peut bénéficier à la performance dans son ensemble, mais il y a un problème de sécurité.
Quelle que soit la configuration, vous pourrez toujours déclencher la réplication de session en appelant setAttribute().
<replication-granularity>
Détermine la granularité des données répliquées. La valeur par défaut est SESSION, mais peut être définie à ATTRIBUTE à la place, pour augmenter la performance dans les sessions où la plupart des attributs restent inchangés.
Les options suivantes ont rarement besoin d'être modifiées.

Tableau 9.2. Options les moins communément modifiées pour les réplications de session

Option
Description
<useJK>
Présumons qu'un équilibreur de charge tel que mod_cluster, mod_jk, ou mod_proxy soit utilisé. La valeur par défaut est false. Si définie sur true, le conteneur examine l'ID de session associé à chaque requête et remplace la partie jvmRoute de l'ID de session en cas de basculement.
<max-unreplicated-interval>
Le plus grand intervalle (en secondes) à attendre après une session avant de déclencher une réplication d'un horodateur de session, même s'il est considéré comme inchangé. Cela permet aux nœuds de cluster d'être informés de l'horodateur de chaque session et aux sessions non répliquées de ne pas expirer de manière incorrecte pendant un basculement. Cela vous permet également de vous fier à une valeur correcte pour les appels à méthode HttpSession.getLastAccessedTime() pendant un basculement.
Par défaut, aucune valeur n'est indiquée. Cela signifie que la configuration jvmRoute du conteneur détermine si le basculement JK est utilisé. Une valeur 0 entraîne une réplication de l'horodateur à chaque accès à la session. Une valeur -1 n'entraîne une réplication de l'horodateur que si une autre activité entraîne une réplication pendant la requête. Une valeur positive supérieure à HttpSession.getMaxInactiveInterval() est traitée comme une erreur de configuration et sera convertie à 0.
<snapshot-mode>
Indique quand les sessions sont répliquées vers d'autres nœuds. La valeur par défaut est INSTANT et l'autre valeur possible est INTERVAL.
En mode INSTANT, les modifications sont répliquées à la fin d'une requête, au moyen du thread de traitement des requêtes. L'option <snapshot-interval> est ignorée.
En mode INTERVAL, une tâche d'arrière-plan s'exécute à l'intervalle indiqué par <snapshot-interval>, et réplique les sessions modifiées.
<snapshot-interval>
L'intervalle, en millisecondes, pendant lequel les sessions modifiées doivent être répliquées si elles utilisent INTERVAL pour la valeur de <snapshot-mode>.
<session-notification-policy>
Nom complet de la classe de l'implémentation de l'interface ClusteredSessionNotificationPolicy qui contrôle si les notifications de spécification servlet sont émises vers un HttpSessionListener, HttpSessionAttributeListener, ou HttpSessionBindingListener enregistré.

9.2. Passivation et activation HttpSession

9.2.1. La passivation et l'activation de session HTTP

La Passivation est le processus de contrôle de l'utilisation de la mémoire, qui consiste au retrait de sessions relativement inutiles de la mémoire quand on les stocke en stockage persistant.
L'Activation signifie que les données sont extraites d'un stockage persistant et sont renvoyées dans la mémoire.
La passivation a lieu à trois reprises au cours du cycle de vie d'une session HTTP :
  • Quand le conteneur réclame la création d'une nouvelle session, si le nombre de sessions actives en cours dépasse une limite configurable, le serveur tentera de passiver certaines sessions pour faire place à une nouvelle session.
  • De façon périodique, à un intervalle précis configuré, une tâche d'arrière-plan vérifiera si les sessions doivent être passivées.
  • Quand une application web est déployée et qu'une copie de sauvegarde de sessions actives sur d'autres serveurs est acquise par le gestionnaire de session de l'application web récemment déployée, les sessions peuvent être passivées.
Une session est passivée si elle remplit les conditions suivantes :
  • La session n'a pas été utilisée pendant une durée plus longue qu'une période d'inactivité configurable maximale.
  • Le nombre de sessions actives dépasse un maximum configurable et la session n'a pas été utilisée pendant une durée plus longue qu'une période d'inactivité configurable maximale.
Les sessions sont toujours passivées en utilisant un algorithme LRU (Least Recently Used).

9.2.2. Configurer la passivation HttpSession dans votre application

Aperçu

La passivation HttpSession est configurée dans le fichier WEB_INF/jboss-web.xml ou META_INF/jboss-web.xml de votre application.

Exemple 9.3. Exemple de fichier jboss-web.xml

<!DOCTYPE jboss-web PUBLIC
    "-//JBoss//DTD Web Application 5.0//EN"
    "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd">

<jboss-web version="6.0"
           xmlns="http://www.jboss.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_6_0.xsd">

   <max-active-sessions>20</max-active-sessions>
   <passivation-config>
      <use-session-passivation>true</use-session-passivation>
      <passivation-min-idle-time>60</passivation-min-idle-time>
      <passivation-max-idle-time>600</passivation-max-idle-time>
   </passivation-config>


</jboss-web>

Éléments de configuration de passivation

<max-active-sessions>
Nombre maximal de sessions actives autorisé. Si le nombre de sessions gérées par le gestionnaire de sessions dépasse cette valeur et que la passivation est activée, ce dépassement sera passivé sur la base du <passivation-min-idle-time> configuré. Ensuite, si le nombre de sessions actives dépasse toujours cette limite, les tentatives de création de nouvelles sessions échoueront. La valeur par défaut de -1 ne définit aucune limite quant au nombre maximal de sessions actives.
<passivation-config>
Cet élément contient le reste des paramètres de configuration de passivation, en tant qu'éléments enfants.

Eléments enfants <passivation-config>

<use-session-passivation>
Indique si oui ou non la passivation de session doit être utilisée. La valeur par défaut est false.
<passivation-min-idle-time>
La durée minimum, en secondes, pendant laquelle une session doit être inactive avant que le conteneur n'envisage de la passiver de manière à réduire le nombre de sessions actives pour être conforme à la valeur définie par les max-active-sessions. La valeur par défaut -1 empêche les sessions d'être passivées avant que le temps de <passivation-max-idle-time> ne se soit écoulé. Les valeurs -1 ou supérieures sont déconseillées si <max-active-sessions> est configuré.
<passivation-max-idle-time>
La durée maximum, en secondes, pendant laquelle une session peut être inactive avant que le conteneur ne tente de la passiver pour économiser de la mémoire. La passivation de ces sessions aura lieu, que le nombre de sessions actives excède <max-active-sessions> ou pas. Cette valeur doit être inférieure à la configuration <session-timeout> dans le fichier web.xml. La valeur par défaut -1 désactive la passivation selon l'inactivité maximum.

Note

Le nombre total de sessions en mémoire comprend les sessions répliquées à partir d'autres nœuds de cluster dont l'accès ne se trouve pas sur ce nœud. Tenez-en compte lors de la configuration de <max-active-sessions>. Le nombre de sessions répliquées à partir d'autres nœuds dépend aussi de savoir si le mode cache REPL ou DIST est désactivé. Dans le mode cache REPL, chaque session est répliquée vers chaque nœud. Dans le mode cache DIST, chaque session n'est répliquée que vers le nombre de nœuds indiqué par le paramètre owner. Veuillez vous référer à Section 9.1.2, « Cache de session web  » et Section 9.1.3, « Configurer le cache de session web  » pour plus de renseignements sur la configuration des modes cache de session.
Prenons comme exemple un cluster à huit nœuds, dont chaque nœud gère les requêtes de 100 utilisateurs. Avec le mode cache REPL, chaque nœud peut stocker 800 sessions en mémoire. Avec le mode cache DIST désactivé, et la configuration owners définie par défaut sur 2, chaque nœud stocke 200 sessions en mémoire.

9.4. Implémenter un Singleton HA

Résumé

La procédure suivante illustre comment déployer un Service qui se trouve dans un decorator de SingletonService et qui est utilisé comme service singleton dans l'ensemble du cluster. Le service active une minuterie programmée, qui n'est lancée qu'à une seule reprise dans le cluster.

Procédure 9.3. Implémenter un Service HA Singleton

  1. Rédiger une application de Service HA Singleton

    Voici un exemple simple de Service se trouvant dans le decorator SingletonService devant être déployé comme service singleton. On trouvera un exemple complet dans le quickstart de cluster-ha-singleton qui est livré avec Red Hat JBoss Enterprise Application Platform 6. Ce quickstart contient toutes les instructions pour générer et déployer l'application.
    1. Créer un service.

      Voici un exemple de service :
      package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
      
      import java.util.Date;
      import java.util.concurrent.atomic.AtomicBoolean;
      
      import javax.naming.InitialContext;
      import javax.naming.NamingException;
      
      import org.jboss.logging.Logger;
      import org.jboss.msc.service.Service;
      import org.jboss.msc.service.ServiceName;
      import org.jboss.msc.service.StartContext;
      import org.jboss.msc.service.StartException;
      import org.jboss.msc.service.StopContext;
      
      
      /**
       * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
       */
      public class HATimerService implements Service<String> {
          private static final Logger LOGGER = Logger.getLogger(HATimerService.class);
          public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton", "timer");
      
          /**
           * A flag whether the service is started.
           */
          private final AtomicBoolean started = new AtomicBoolean(false);
      
          /**
           * @return the name of the server node
           */
          public String getValue() throws IllegalStateException, IllegalArgumentException {
              LOGGER.infof("%s is %s at %s", HATimerService.class.getSimpleName(), (started.get() ? "started" : "not started"), System.getProperty("jboss.node.name"));
              return "";
          }
      
          public void start(StartContext arg0) throws StartException {
              if (!started.compareAndSet(false, true)) {
                  throw new StartException("The service is still started!");
              }
              LOGGER.info("Start HASingleton timer service '" + this.getClass().getName() + "'");
      
              final String node = System.getProperty("jboss.node.name");
              try {
                  InitialContext ic = new InitialContext();
                  ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).initialize("HASingleton timer @" + node + " " + new Date());
              } catch (NamingException e) {
                  throw new StartException("Could not initialize timer", e);
              }
          }
      
          public void stop(StopContext arg0) {
              if (!started.compareAndSet(true, false)) {
                  LOGGER.warn("The service '" + this.getClass().getName() + "' is not active!");
              } else {
                  LOGGER.info("Stop HASingleton timer service '" + this.getClass().getName() + "'");
                  try {
                      InitialContext ic = new InitialContext();
                      ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).stop();
                  } catch (NamingException e) {
                      LOGGER.error("Could not stop timer", e);
                  }
              }
          }
      }
      
    2. Créer un activator qui installe le Service en tant que singleton clusterisé.

      La liste suivante est un exemple d'activateurr de service qui installe le HATimerService comme service singleton clusterisé :
      package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
      
      import org.jboss.as.clustering.singleton.SingletonService;
      import org.jboss.logging.Logger;
      import org.jboss.msc.service.DelegatingServiceContainer;
      import org.jboss.msc.service.ServiceActivator;
      import org.jboss.msc.service.ServiceActivatorContext;
      import org.jboss.msc.service.ServiceController;
      
      
      /**
       * Service activator that installs the HATimerService as a clustered singleton service
       * during deployment.
       *
       * @author Paul Ferraro
       */
      public class HATimerServiceActivator implements ServiceActivator {
          private final Logger log = Logger.getLogger(this.getClass());
      
          @Override
          public void activate(ServiceActivatorContext context) {
              log.info("HATimerService will be installed!");
      
              HATimerService service = new HATimerService();
              SingletonService<String> singleton = new SingletonService<String>(service, HATimerService.SINGLETON_SERVICE_NAME);
              /*
               * To pass a chain of election policies to the singleton, for example, 
               * to tell JGroups to prefer running the singleton on a node with a
               * particular name, uncomment the following line:
               */
              // singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node2/cluster")));
      
              singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
                      .setInitialMode(ServiceController.Mode.ACTIVE)
                      .install()
              ;
          }
      }
      

      Note

      L'exemple de code ci-dessus utilise une classe, org.jboss.as.clustering.singleton.SingletonService, qui fait partie de l'API privée de JBoss EAP. Une API publique deviendra disponible dans la version EAP 7 et les classes privées seront obsolètes, mais ces classes seront entretenues et rendues disponible pendant toute la durée du cycle de version EAP 6.x.
    3. Créer un fichier ServiceActivator

      Créer un fichier nommé org.jboss.msc.service.ServiceActivator dans le répertoire resources/META-INF/services/ de l'application. Ajouter une ligne contenant le nom complet de la classe de Service Activator créée dans l'étape précédente.
      org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.HATimerServiceActivator 
    4. Créer un bean singleton qui implémente une minuterie de singleton à utiliser dans tout le cluster.

      Ce bean Singleton ne doit pas avoir d'interface distante et ne doit pas référencer son interface locale à partir d'un autre EJB d'application. Cela évite une recherche client ou de la part d'un autre composant et assure un parfait contrôle du Singleton par le SingletonService.
      1. Créer une interface de planification.

        package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
        
        /**
         * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
         */
        public interface Scheduler {
        
            void initialize(String info);
        
            void stop();
        
        }
        
      2. Créer le bean Singleton qui implémente la minuterie de singleton à utiliser dans tout le cluster.

        package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
        
        import javax.annotation.Resource;
        import javax.ejb.ScheduleExpression;
        import javax.ejb.Singleton;
        import javax.ejb.Timeout;
        import javax.ejb.Timer;
        import javax.ejb.TimerConfig;
        import javax.ejb.TimerService;
        
        import org.jboss.logging.Logger;
        
        
        /**
         * A simple example to demonstrate a implementation of a cluster-wide singleton timer.
         *
         * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
         */
        @Singleton
        public class SchedulerBean implements Scheduler {
            private static Logger LOGGER = Logger.getLogger(SchedulerBean.class);
            @Resource
            private TimerService timerService;
        
            @Timeout
            public void scheduler(Timer timer) {
                LOGGER.info("HASingletonTimer: Info=" + timer.getInfo());
            }
        
            @Override
            public void initialize(String info) {
                ScheduleExpression sexpr = new ScheduleExpression();
                // set schedule to every 10 seconds for demonstration
                sexpr.hour("*").minute("*").second("0/10");
                // persistent must be false because the timer is started by the HASingleton service
                timerService.createCalendarTimer(sexpr, new TimerConfig(info, false));
            }
        
            @Override
            public void stop() {
                LOGGER.info("Stop all existing HASingleton timers");
                for (Timer timer : timerService.getTimers()) {
                    LOGGER.trace("Stop HASingleton timer: " + timer.getInfo());
                    timer.cancel();
                }
            }
        }
        
  2. Démarrez chaque instance de JBoss EAP 6 avec le clustering activé.

    Pour activer le clustering dans les serveurs autonomes, démarrer chaque serveur par le profil HA, en utilisant un nom de code unique et un offset (décallage) de port pour chaque instance comme suit :
    • Dans Linux, on utilise la commande suivante pour démarrer les serveurs :
      EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET

      Exemple 9.5. Démarrage de multiples serveurs autonomes sur Linux

      $ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node1
      $ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
    • Dans Microsoft Windows, on utilise la commande suivante pour démarrer les serveurs :
      EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET

      Exemple 9.6. Démarrage de multiples serveurs autonomes dans Microsoft Windows

      C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node1
      C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100

    Note

    Si vous ne souhaitez pas utiliser des arguments en ligne de commande, vous pouvez configurer le fichier standalone-ha.xml pour que chaque instance de serveur puisse se lier à une interface séparée.
  3. Déployer l'application dans les serveurs

    Si vous utilisez la commande Maven suivante pour déployer votre application dans un serveur autonome qui exécute sur les ports par défaut.
    mvn clean install jboss-as:deploy
    Pour déployer des serveurs supplémentaires, indiquer le nom du serveur. S'il est sur un hôte différent, passez le nom d'hôte et le numéro de port dans la ligne de commande :
    mvn clean package jboss-as:deploy -Djboss-as.hostname=localhost -Djboss-as.port=10099
    Voir le quickstart cluster-ha-singleton fourni dans JBoss EAP 6 pour la configuration et les détails de déploiement de Maven.

9.5. Application Apache mod_cluster-manager

9.5.1. L'application mod_cluster-manager

L'application mod_cluster-manager est une page web d'administration qui est disponible sur le serveur HTTP Apache. Elle est utilisée pour surveiller les nœuds de worker connectés et pour effectuer diverses tâches d'administration comme l'activation/la désactivation de contextes et pour configurer les propriétés d'équilibrage de charge de nœuds de worker dans un cluster.

9.5.2. L'application mod_cluster-manager

L'application mod_cluster-manager peut être utilisée pour effectuer un certain nombre de tâches administratives sur les noeuds de worker.
Le schéma ci-dessous montre la page web de l'application mod_cluster-manager contenant des annotations qui mettent en relief les composants importants et les options d'administration sur cette page.
Description

Figure 9.1. La page web d'administration de mod_cluster

Les annotations sont expliquées ci-dessous :
  • [1] mod_cluster/1.2.8.Final : indique la version de la bibliothèque de mod_cluster native
  • [2] ajp://192.168.122.204:8099: indique le protocole utilisé (un parmi AJP, HTTP, HTTPS), le nom d'hôte ou l'adresse IP d'un noeud de worker et le port
  • [3] jboss-eap-6.3-2: indique la JVMRoute du noeud de worker.
  • [4] Virtual Host 1: indique le(s) hôte(s) virtuel(s) configuré(s) sur le noeud de worker
  • [5] Disable : il s'agit d'une option administrative qui peut être utilisée pour désactiver la création de nouvelles sessions dans un contexte particulier. Cependant, les sessions courantes ne sont pas désactivées et demeurent intactes.
  • [6] Stop : il s'agit d'une option administrative qui peut être utilisée pour stoper la redirection de demandes de sessions au contexte. Les sessions restantes seront reprises par défaut par un autre noeud à moins que la propriété sticky-session-force soit définie sur "true"
  • [7] Enable Contexts Disable Contexts Stop Contexts (Activer les contextes Désativer les contextes Stopper les contextes) : opérations qui peuvent être effectuées sur tout le noeud. En sélectionnant une de ces options, vous affectez tous les contextes de noeuds dans tous ses hôtes virtuels.
  • [8] Load balancing group (LBGroup): la propriété load-balancing-group est définie dans le sous-système mod_cluster dans la configuration EAP pour regrouper tous les noeuds de worker en groupes d'équilibrage de charges personnalisés. Le groupe d'équilibrage de charge (LBGroup) est un champ informel qui donne des informations sur tous les groupes d'équilibrage de charge définis. Si ce champ n'est pas défini, alors tous les noeuds de worker sont regroupés en un seul groupe d'équilibrage de charges par défaut.

    Note

    Il s'agit d'un champ informationnel qui ne peut donc pas être utilisé pour définir la propriété load-balancing-group. Cette propriété doit être définie dans le sous-système mod_cluster dans la configuration EAP.
  • [9] Load (value) : indique la charge sur le noeud de worker. Les « load factors » ou facteurs de charge sont évalués ainsi :
    -load > 0  : un facteur de charge de 1 indique une surcharge du noeud de worker, 100 indique qu'il est libre de toute charge.
    -load = 0  : un facteur de charge de 0 indique que le noeud de worker est en attente, c-a-d qu'aucune demande de session ne sera redirigée vers ce noeud sauf si les autres noeuds de worker ne sont pas disponibles
    -load = -1 : un facteur de charge de -1 indique qu'il y a une erreur dans l'état du noeud de worker.
    -load = -2 : un facteur de charge de -2 indique que le noeud de worker est en cours de CPing/CPong et se trouve dans un état de transition
    

Chapitre 10. CDI

10.1. CDI

10.1.2. CDI (Contexts and Dependency Injection)

DCI (Context and Dependency Injection) est une spécification conçue pour permettre aux composants EJB 3.0 "d'être utilisés comme beans JSF (java Server Faces), réunissant les deux modèles de composants et permettant la simplification du modèle de programmation des applications basées-web dans Java". La citation suivante est extraite de la spécification JSR-299, que l'on peut trouver à http://www.jcp.org/en/jsr/detail?id=299.
JBoss EAP 6 inclut Weld, qui est l'implementation de référence de JSR-299. Pour obtenir plus d'informations sur l'injection de dépendance type-safe, voir Section 10.1.4, « Injection de dépendance de Type-safe ».

10.1.3. Avantages de CDI

  • CDI simplifie et rétrécit votre base code en remplaçant des gros morceaux de code par des annotations.
  • CDI est flexible, ce qui vous permet de désactiver ou d'activer des injections et des événements, d'utiliser des autres beans, ou d'injecter des objets non-CDI facilement.
  • Il est facile d'utiliser votre ancien code avec CDI. Vous n'avez qu'à y inclure un beans.xml dans votre répertoire META-INF/ ou WEB-INF/. Le fichier peut être vide.
  • CDI simplifie le packaging et les déploiements, et réduit le montant d'XML que vous aurez besoin d'ajouter à vos déploiements.
  • CDI propose une gestion du cycle de vie par contexte. Vous pouvez lier des injections aux requêtes, sessions, conversations, ou contextes personnalisés.
  • CDI propose une injection de dépendance type-safe, qui est plus sécurisée et facile à débogger qu'une injection basée string.
  • CDI découple les intercepteurs des beans.
  • CDI fournit une notification d'événements complexe.

10.1.4. Injection de dépendance de Type-safe

Avant JSR-299 et DCI, la seule façon d'injecter des dépendances dans Java était par chaînes. Cela portait à erreur. DCI a introduit la possibilité d'injecter des dépendances d'une manière plus sûre.

10.1.5. Relation entre Weld, Seam 2, Seam 3, et JavaServer Faces

Le but de Seam 2 était d'unifier les beans gérés Enterprise Java Beans (EJBs) et JavaServer Faces (JSF).
JavaServer Faces (JSF) implémente JSR-314. Il s'agit d'un API qui construit les interfaces utilisateur côté serveur. JBoss Web Framework Kit inclut RichFaces, qui est une implémentation de JavaServer Faces et d'AJAX.
Weld est l'implémentation de référence de Contexts and Dependency Injection (CDI), définie dans JSR-299. Weld est inspiré de Seam 2 et d'autres frameworks d'injection de dépendances. Weld est inclus dans JBoss EAP 6.

10.2. Utiliser CDI

10.2.1. Premières étapes

10.2.1.1. Activer CDI

Résumé

Le CDI (Contexts and Dependency Injection) est l'une des technologies de base de la JBoss EAP 6, et est activé par défaut. Si, pour une raison ou une autre, il était désactivé et que vous souhaitiez l'activer, veuillez suivre le procédé suivant :

Procédure 10.1. Activer le CDI dans JBoss EAP 6

  1. Vérifier si les informations du sous-système sont dé-commentées dans le fichier de configuration.

    On peut désactiver un sous-système en dé-commentant la section qui convient dans les fichiers de configuration domain.xml ou standalone.xml, ou en supprimant la section qui convient.
    Pour trouver le sous-système dans EAP_HOME/domain/configuration/domain.xml ou EAP_HOME/standalone/configuration/standalone.xml, le chercher dans les strings suivants. S'il existe, il se trouve dans la section <extensions>.
    <extension module="org.jboss.as.weld"/>
    La ligne suivante doit se trouver dans le profil que vous utilisez. Les profils se trouvent dans des éléments de <profile> dans la section <profiles>.
    <subsystem xmlns="urn:jboss:domain:weld:1.0"/>
  2. Avant de modifier un fichier, arrêter JBoss EAP 6.

    JBoss EAP 6 modifie les fichiers de configuration pendant le temps d'exécution, vous devez donc stopper le serveur avant de modifier directement les fichiers de configuration.
  3. Modifer le fichier de configuration pour restaurer le sous-système CDI.

    Si le sous-système CDI est dé-commenté, supprimer les commentaires.
    S'il était supprimé totalement, le restaurer en ajoutant cette ligne au fichier dans une nouvelle ligne directement au dessus de la balise </extensions> :
    <extension module="org.jboss.as.weld"/>
  4. Vous devez également ajouter la ligne suivante dans le profil concerné qui se trouve dans la section <profiles>.
    <subsystem xmlns="urn:jboss:domain:weld:1.0"/>
  5. Démarrer à nouveau JBoss EAP 6.

    Démarrer JBoss EAP 6 avec votre configuration mise à jour.
Résultat

JBoss EAP 6 démarre avec le sous-système CDI activé.

10.2.2. Utiliser CDI pour développer une application

10.2.2.2. CDI avec le code existant

Presque chaque classe concrète de Java ayant un constructeur sans paramètre, ou un constructeur désigné avec l'annotation @Inject, est un bean. La seule chose que vous devez faire avant de commencer à injecter des beans est de créer un fichier appelé beans.xml dans le répertoire META-INF/ ou WEB-INF/ de votre archive. Ce fichier peut rester vide.

Procédure 10.2. Utiliser les anciens beans dans les applications CDI

  1. Groupez vos beans dans une archive.

    Groupez vos beans dans une archive JAR ou WAR.
  2. Inclure un fichier beans.xml dans votre archive.

    Mettez un fichier beans.xml dans votre répertoire d'archive JAR META-INF/ ou d'archive WAR WEB-INF/. Ce fichier peut rester vide.
Résultat  :

Vous pouvez utiliser ces beans avec CDI. Le conteneur peut créer et détruire des instances de vos beans et les associer à un contexte désigné, les injecter dans les autres beans, les utiliser dans des expressions EL, les spécialiser par des annotations de qualificateur et y ajouter des intercepteurs et des décorateurs, sans aucune modification de votre code existant. Dans certains cas, vous devrez peut-être ajouter des annotations.

10.2.2.3. Exclure les beans du processus de balayage

Résumé

Une des caractéristiques de Weld, l'implémentation de CDI de JBoss EAP 6, est la possibilité d'exclure des classes de balayage de votre archive, d'avoir des événements de cycle de vie conteneur émis, et déployés comme beans. Cela ne fait pas partie de la spécification JSR-299.

Exemple 10.1. Exclure des packages de votre bean.

L'exemple suivant possède plusieurs balises <weld:exclude>.
  1. Le premier exclut toutes les classes Swing.
  2. Le second exclut les classes Google Web Toolkit si le Google Web Toolkit n'est pas installé.
  3. Le troisième exclut les classes qui finissent dans la chaîne Blether (en utilisant une expression régulière), si la propriété de système verbosity est définie comme low.
  4. Le quatrième exclut les classes JSF (Java Server Faces) si les classes Wicket sont présentes et la propriété de système viewlayer n'est pas définie.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:weld="http://jboss.org/schema/weld/beans" 
       xsi:schemaLocation="
          http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd
          http://jboss.org/schema/weld/beans http://jboss.org/schema/weld/beans_1_1.xsd">
    
    <weld:scan>
      
        <!-- Don't deploy the classes for the swing app! -->
        <weld:exclude name="com.acme.swing.**" />
      
        <!-- Don't include GWT support if GWT is not installed -->
        <weld:exclude name="com.acme.gwt.**">
            <weld:if-class-available name="!com.google.GWT"/>
        </weld:exclude>
        
        <!--
            Exclude classes which end in Blether if the system property verbosity is set to low
            i.e.
              java ... -Dverbosity=low            
        -->        
        <weld:exclude pattern="^(.*)Blether$">
            <weld:if-system-property name="verbosity" value="low"/>
        </weld:exclude>
        
       <!--
             Don't include JSF support if Wicket classes are present, and the viewlayer system
             property is not set
        -->
        <weld:exclude name="com.acme.jsf.**">
            <weld:if-class-available name="org.apache.wicket.Wicket"/>
            <weld:if-system-property name="!viewlayer"/>
        </weld:exclude>
    </weld:scan>
</beans>
La spécification officielle des options de configuration spéciale Weld se trouve dans http://jboss.org/schema/weld/beans_1_1.xsd.

10.2.2.4. Utiliser une injection pour étendre une implémentation

Résumé

Vous pouvez utiliser une injection pour ajouter ou modifier une caractéristique de votre code existant. Cet exemple vous montre comment ajouter une capacité de traduction à une classe existante. La traduction est une caractéristique hypothétique et la façon dont elle est implémentée dans cet exemple est par pseudo-code, et elle est fournie à titre illustratif uniquement.

L'exemple part du principe que vous avez déjà une classe Welcome, qui possède une méthode buildPhrase. La méthode buildPhrase utilise le nom d'une ville comme paramètre et crée une formule telle que "Welcome to Boston". Votre objectif est de créer une version de cette classe Welcome pouvant traduire cette formule d'accueil dans une autre langue.

Exemple 10.2. Injecter un Bean Translator dans la classe Welcome.

Le pseudo-code suivant injecte un objet Translator hypothétique dans la classe Welcome. L'objet Translator peut être un bean sans état EJB ou un autre type de bean, pouvant traduire des phrases d'une langue à une autre. Ainsi, le Translator est utilisé pour traduire la formule d'accueil complète, sans avoir à modifier la classe Welcome d'origine. Le Translator est injecté avant que la méthode buildPhrase ne soit implémentée.
L'échantillon de code ci-dessous est un exemple de classe Welcome de Traduction.
public class TranslatingWelcome extends Welcome {

    @Inject Translator translator;

    public String buildPhrase(String city) {
        return translator.translate("Welcome to " + city + "!");
    }
    ...
}

10.2.3. Dépendances ambigues ou non satisfaites

10.2.3.1. Dépendances ambigües et non-satisfaites

On parle de dépendances ambigües quand le conteneur ne peut pas résoudre une injection dans un bean précisément.
On parle de dépendances non-satisfaites quand le conteneur ne peut pas résoudre une injection dans aucun bean.
Le conteneur suit les étapes suivantes pour essayer de résoudre les dépendances :
  1. Il résout les annotations de qualificateur sur tous les beans qui implémentent le bean d'un point d'injection.
  2. Il filre les beans désactivés. Les beans désactivés sont des beans @Alternative qui ne sont pas explicitement activés.
Dans le cas d'une dépendance ambigüe ou non satisfaite, le conteneur stoppe le déploiement et envoie une exception.

10.2.3.2. Qualificateurs

Un qualificateur est une annotation qui relie un bean à un autre. Cela vous permet d'indiquer avec précision quel bean vous souhaitez injecter. Les qualificateurs possèdent une rétention et une cible, définies comme dans l'exemple ci-dessous.

Exemple 10.3. Définit les qualificateurs @Synchronous et @Asynchronous

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Synchronous {}
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Asynchronous {}

Exemple 10.4. Utiliser les qualificateurs @Synchronous et @Asynchronous

@Synchronous
public class SynchronousPaymentProcessor implements PaymentProcessor {

   public void process(Payment payment) { ... }

}
@Asynchronous
public class AsynchronousPaymentProcessor implements PaymentProcessor {

   public void process(Payment payment) { ... }
}

10.2.3.3. Utiliser un qualificateur pour résoudre une injection ambiguë

Résumé

Cette tâche indique une injection ambiguë et la retire par un qualificateur. Pour en savoir plus sur les injections ambiguës, voir Section 10.2.3.1, « Dépendances ambigües et non-satisfaites ».

Exemple 10.5. Injections ambiguës

Il y a deux implémentations de la classe Welcome, une qui traduit, et l'autre non. Dans cette situation, l'injection ci-dessous est ambiguë et a besoin d'être spécifiée pour qu'on puisse utiliser la classe de traduction Welcome.
public class Greeter {
  private Welcome welcome;

  @Inject
  void init(Welcome welcome) {
    this.welcome = welcome;
  }
  ...
}

Procédure 10.3. Résoudre une injection ambiguë avec un qualificateur

  1. Créer une annotation de qualificateur nommée @Translating.

    @Qualifier
    @Retention(RUNTIME)
    @Target({TYPE,METHOD,FIELD,PARAMETERS})
    public @interface Translating{}
    
  2. Annoter votre classe Welcome par l'annotation @Translating.

    @Translating
    public class TranslatingWelcome extends Welcome {
        @Inject Translator translator;
        public String buildPhrase(String city) {
            return translator.translate("Welcome to " + city + "!");
        }
        ...
    }
    
  3. Demander la classe de traduction Welcome dans votre injection.

    Vous devez demander une implémentation explicitement qualifiée, qui ressemble au modèle de la méthode de fabrique. L'ambiguité se résout au point d'injection.
    public class Greeter {
      private Welcome welcome;
      @Inject
      void init(@Translating Welcome welcome) {
        this.welcome = welcome;
      } 
      public void welcomeVisitors() {
        System.out.println(welcome.buildPhrase("San Francisco"));
      }
    }
    
Résultat

TranslatingWelcome est utilisé, et il n'y a pas d'ambiguité.

10.2.4. Beans gérés

10.2.4.1. Les beans gérés

Avant Java EE 6, il y n'avait aucun définition précise du terme bean dans la plate-forme Java EE. Il y avait plusieurs concepts que l'on nommait « bean » dans les spécifications de Java EE, comme les EJB beans et les JSF managed beans. Les frameworks de tierce partie comme Spring et Seam ont commencé à nous donner leur avis sur ce que l'on entend par bean.
Java EE 6 a établi une définition commune dans la spécification de Managed Beans. Les Managed Beans sont définis comme des objets gérés par conteneur avec des restrictions minimales de programmation, connus aussi sous le sigle POJO (Plain Old Java Object). Ils supportent un petit ensemble de services de base, tels que l'injection de ressources, les rappels de cycle de vie et les intercepteurs. Les spécifications de leurs accompagnateurs, comme les EJB et CDI, s'appuient sur ce modèle de base.
À part quelques exceptions, chaque classe concrète de Java qui a un constructeur sans paramètre (ou un constructeur désigné avec l'annotation @Inject) est un bean. Cela inclut chaque JavaBean et chaque bean de session EJB. La seule exigence pour activer les services mentionnés dans les beans est qu'ils résident dans une archive (un JAR ou un module Java EE comme un WAR ou JAR EJB) qui contienne un fichier spécial marqueur : META-INF/beans.xml.

10.2.4.2. Types de Classes qui ne sont pas des Beans

Un bean géré est une classe Java. Le cycle de base et la sémantique d'un bean géré est définie par la spécification de beans gérés. Vous pouvez explicitement déclarer un bean géré en annotant la classe bean @ManagedBean, mais en CDI cela n'est pas nécessaire. Conformément à la spécification, le conteneur CDI traite n'importe quelle classe qui remplit les conditions suivantes en tant que bean géré :
  • Il s'agit d'une classe interne non-statique
  • Il ne s'agit pas d'une classe concrète, ou bien elle est annotée par @Decorator
  • Elle n'est pas annotée par une annotation définie par un composant EJB, ni déclarée comme classe bean EJB dans ejb-jar.xml.
  • Elle n'implémente pas l'interface javax.enterprise.inject.spi.Extension.
  • Elle possède soit un constructeur sans paramètre soit un constructeur annoté avec @Inject.
L'ensemble de types de beans non-restraints d'un bean géré contient la classe de bean, toutes les superclasses et interfaces qu'il implémente directement ou indirectement.
Si un bean géré contient un champ public, il devra avoir comme scope par défaut @Dependent.

10.2.4.3. Utiliser le CDI pour injecter un objet dans un bean

Quand votre archive de déploiement inclut un fichier META-INF/beans.xml ou WEB-INF/beans.xml, chaque objet de votre déploiement peut être injecté par le CDI.
Cette procédure vous explique les moyens les plus courants d'injecter des objets dans d'autres objets.
  1. Injecter un objet dans n'importe quelle partie de bean par l'annotation @Inject.

    Pour obtenir une instance de classe, dans votre bean, annotez ce champ avec @Inject.

    Exemple 10.6. Injecter une instance TextTranslator dans un TranslateController

    public class TranslateController {
    
       @Inject TextTranslator textTranslator;
       ...
    
  2. Utiliser les méthodes de l'objet injecté

    Vous pouvez directement utiliser les méthodes de vos objets injectés. Partez du principe que TextTranslator possède une méthode translate.

    Exemple 10.7. Utiliser les méthodes de l'objet injecté

    // in TranslateController class
    
    public void translate() {
    
       translation = textTranslator.translate(inputText); 
    
    }
    
  3. Utiliser l'injection dans le contructeur d'un bean

    Vous pouvez injecter des objets dans le constructeur d'un bean, ou bien, vous pouvez utiliser une fabrique ou un localisateur de service pour les créer.

    Exemple 10.8. Utiliser l'injection pour la construction d'un bean

    public class TextTranslator {
    
       private SentenceParser sentenceParser;
    
       private Translator sentenceTranslator;
    
        
    
       @Inject
    
       TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
    
          this.sentenceParser = sentenceParser;
    
          this.sentenceTranslator = sentenceTranslator;
    
       }
    
       // Methods of the TextTranslator class
       ...
    }
    
  4. Utiliser l'interface Instance(<T>) pour obtenir les instances de façon programmatique.

    L'interface Instance peut renvoyer une instance de TextTranslator si paramétrée par le type de bean.

    Exemple 10.9. Obtenir une instance par programmation

    @Inject Instance<TextTranslator> textTranslatorInstance;
    
    ...
    
    public void translate() {
    
       textTranslatorInstance.get().translate(inputText);
    
    }
    
Résultat  :

Lorsque vous injectez un objet dans un bean, toutes les méthodes et les propriétés de l'objet sont disponibles pour votre bean. Si vous injectez dans le constructeur de votre bean, les instances des objets injectés sont créés quand le constructeur de votre bean est appelé, à moins que l'injection ne fasse référence à une instance qui existe déjà. Par exemple, une nouvelle instance ne serait pas créée si vous injectiez un bean basé-session pendant la durée de la session.

10.2.5. Contextes, Scopes et Dépendances

10.2.5.1. Contextes et scopes

Un contexte, en terme de CDI, est une zone de stockage qui contient des instances de beans associées à un scope particulier.
Un scope est un lien entre un bean et un contexte. Un scope/context peut avoir un cycle de vie particulier. Il existe plusieurs scopes pré-définis, et vous pouvez créer vos propres scopes. Voici des exemples de scopes pré-définis @RequestScoped, @SessionScoped, et @ConversationScope.

10.2.5.2. Contextes disponibles

Tableau 10.1. Contextes disponibles

Contexte Description
@Dependent Le bean est lié au cycle de vie du bean qui contient la référence.
@ApplicationScoped Lié au cycle de vie de l'application.
@RequestScoped Lié au cycle de vie de la requête.
@SessionScoped Lié au cycle de vie de la session.
@ConversationScoped Lié au cycle de vie de la conversation. Le scope de la conversation correspond aux longueurs de la requête et de la session, et est contrôlé par l'application.
Scopes personnalisés Si les contextes ne correspondent pas à vos besoins, vous pourrez définir des scopes personnalisés.

10.2.6. Cycle de vie d'un bean

10.2.6.1. Gestion du cycle de vie d'un bean

Résumé

Cette tâche vous montre comment sauvegarder un bean pendant la durée de vie d'une requête. Il existe plusieurs autres scopes, et vous pouvez définir vos propres scopes.

Le scope par défaut d'un bean injecté est @Dependent. Cela veut dire que le cycle de vie du bean dépend du cycle de vie du bean qui contient la référence. Pour plus d'informations, voir Section 10.2.5.1, « Contextes et scopes ».

Procédure 10.4. Gestion du cycle de vie d'un bean

  1. Annoter le bean par le scope qui correspond au scope désiré.

    @RequestScoped
    @Named("greeter")
    public class GreeterBean {
      private Welcome welcome;
      private String city; // getter & setter not shown
      @Inject   void init(Welcome welcome) {
        this.welcome = welcome;
      }
      public void welcomeVisitors() {
        System.out.println(welcome.buildPhrase(city));
      }
    }
    
  2. Quand votre bean est utilisé dans la vue JSF, il contient un état.

    <h:form>
      <h:inputText value="#{greeter.city}"/>
      <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/>
    </h:form>
Résultat  :

Votre bean est sauvegardé dans le contexte lié au scope que vous spécifiez, et dure aussi longtemps que le scope est applicable.

10.2.6.2. Utilisation d'une méthode Producer

Résumé

Cette tâche vous montre comment utiliser les méthodes Producer pour créer un certain nombre d'objets qui ne sont pas des beans, pour l'injection.

Exemple 10.10. Utiliser une méthode Producer au lieu d'une méthode Alternative, pour permettre le polymorphisme suite au déploiement.

L'annotation @Preferred dans l'exemple donné est une annotation qualifiante. Pour plus d'informations sur les qualificateurs, veuillez vous référer à : Section 10.2.3.2, « Qualificateurs ».
@SessionScoped
public class Preferences implements Serializable {
   private PaymentStrategyType paymentStrategy;
   ...
   @Produces @Preferred 
   public PaymentStrategy getPaymentStrategy() {
       switch (paymentStrategy) {
           case CREDIT_CARD: return new CreditCardPaymentStrategy();
           case CHECK: return new CheckPaymentStrategy();
           default: return null;
       } 
   }
}
Le point d'injection suivant a les mêmes type et qualificateur d'annotations que la méthode de Producer, donc il se résout à la méthode Producer en utilisant les règles usuelles d'injection CDI. La méthode Producer est appelée par le conteneur pour obtenir une instance au service de ce point de l'injection.
@Inject @Preferred PaymentStrategy paymentStrategy;

Exemple 10.11. Assigner un scope à une méthode Producer

L'étendue par défaut d'une méthode Producer est @Dependent. Si vous attribuez un scope à un bean, celle-ci est destinée au contexte approprié. La méthode Producer dans cet exemple n'est appelée qu'une fois par session.
@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
   ...
}

Exemple 10.12. Utiliser une injection à l'intérieur d'une méthode Producer

Les objets instanciés directement par une application ne peuvent pas profiter de l'injection de dépendance, et n'ont pas d'intercepteurs. Cependant, vous pouvez utiliser l'injection de dépendance dans la méthode Producer pour obtenir des instances de bean.
@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          CheckPaymentStrategy cps ) {
   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      default: return null;
   } 
}

Si vous injectez un bean de demande (request-scoped) dans un Producer de session (session-scoped), la méthode Producer favorise l'instance actuelle de demande (request-scoped) dans le scope de la session. Ce n'est certainement pas le comportement souhaité, alors soyez prudent lorsque vous utilisez une méthode Producer de cette façon.

Note

Le scope de la méthode Producer ne provient pas du bean qui déclare la méthode producer.
Résultat

Les méthodes Producer vous permettent d'injecter des objets non-bean et de changer votre code de façon dynamique.

10.2.7. Beans nommés et beans alternatifs

10.2.7.1. Named Beans

Un Named Bean est un bean nommé qui utilise l'annotation @Named. Nommer un bean vous permet de l'utiliser directement dans JSF (Java Server Faces).
L'annotation @Named prend un paramètre optionnel, qui correspond au nom du bean. Si le paramètre est omis, le nom du bean en minuscules sera utilisé.

10.2.7.2. Utilisation des Named Beans

  1. Utiliser l'annotation @Named pour assigner un nom à un bean.

    @Named("greeter")
    public class GreeterBean {
      private Welcome welcome;
    
      @Inject
      void init (Welcome welcome) {
        this.welcome = welcome;
      }
    
      public void welcomeVisitors() {
        System.out.println(welcome.buildPhrase("San Francisco"));
      }
    }
    
    Le nom du bean en soi est une option. Si vous l'oubliez, le bean sera nommé par rapport à son nom de classe, avec la première lettre non capitalisée. Dans l'exemple ci-dessus, le nom par défaut serait donc greeterBean.
  2. Utiliser le bean nommé dans une vue JSF

    <h:form>
      <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/>
    </h:form>
Résultat  :

Votre bean nommé correspond à une action dans le contrôle de votre vue JSF, et à un minimum de code.

10.2.7.3. Beans Alternative

Les beans Alternative sont des beans qui possèdent une implémentation spécifique à un modèle de client particulier ou à un scénario de déploiement particulier.

Exemple 10.13. Définition d'alternative

Cet alternative se rapporte à une implémentation factice d'à la fois un @Synchronous PaymentProcessor et d'un @Asynchronous PaymentProcessor, en un :
@Alternative @Synchronous @Asynchronous

public class MockPaymentProcessor implements PaymentProcessor {

   public void process(Payment payment) { ... }

}
Par défaut, les beans @Alternative sont désactivés. Ils sont activés pour une archive bean particulière grâce à la modification du fichier beans.xml.

10.2.7.4. Remplacer une injection par une alternative

Résumé

Les beans alternatifs vous permettent de remplacer des beans existants. On peut les concevoir comme un moyen d'ajouter une classe qui remplit le même rôle, mais qui fonctionne différemment. Ils sont désactivés par défaut. Cette tâche vous montre comment indiquer et activer une alternative.

Procédure 10.5. Remplacer une injection

Cette tâche part du principe que vous possédez déjà une classe TranslatingWelcome dans votre projet, mais que vous souhaitez la remplacer par une "fausse" classe TranslatingWelcome. Cela serait le cas pour un déploiement test, où le vrai bean Translator ne peut être utilisé.
  1. Définir l'alternative.

    @Alternative
    @Translating 
    public class MockTranslatingWelcome extends Welcome {
      public String buildPhrase(string city) {
        return "Bienvenue à " + city + "!");
      }
    }
    
  2. Substituer l'alternative.

    Pour activer l'implémentation de substitution, ajouter le nom complet de la classe à votre fichier META-INF/beans.xml ou WEB-INF/beans.xml.
    <beans>
      <alternatives>
        <class>com.acme.MockTranslatingWelcome</class>
      </alternatives>
    </beans>
Résultat

L'implémentation alternative est maintenant utilisée à la place de l'implémentation d'origine.

10.2.8. Stérétypes

10.2.8.1. Stéréotypes

Dans de nombreux systèmes, l'utilisation de modèles architecturaux produit un ensemble de rôles de beans récurrents. Un stéréotype vous permet d'identifier un tel rôle et de déclarer certaines métadonnées communes pour les beans avec ce rôle dans un emplacement centralisé.
Un stéréotype englobe toute combinaison de :
  • scope par défaut
  • un groupe de liaisons d'intercepteur
Un stéréotype peut également indiquer un des deux scénarios :
  • tous les beans avec un stéréotype ont des noms EL de bean par défaut
  • tous les beans avec un stéréotype sont des alternatifs
Un bean peut déclarer zéro, un ou plusieurs stéréotypes. Les annotations de stéréotype peuvent être appliquées à une classe de bean, à une méthode Producer ou à un champ.
Un stéréotype est une annotation, annotée @Stereotype, qui regroupe plusieurs autres annotations.
Une classe qui hérite d'un scope d'un stéréotype peut remplacer ce stéréotype et spécifier un scope directement sur le bean.
De plus, si un stéréotype possède une annotation @Named, tout bean sur lequel elle se trouve a un nom de bean par défaut. Le bean peut remplacer ce nom si l'annotation @Named est directement spécifiée sur le bean. Pour plus d'informations sur les beans nommés, voir Section 10.2.7.1, « Named Beans ».

10.2.8.2. Utilisation des stéréotypes

Résumé

Sans les stéréotypes, les annotations peuvent devenir encombrées. Cette tâche vous montre comment utiliser les stéréotypes pour réduire l'encombrement et simplifier votre code. Pour plus d'informations sur ce que sont les stéréotypes, voir Section 10.2.8.1, « Stéréotypes ».

Exemple 10.14. Encombrement d'annotation

@Secure
@Transactional
@RequestScoped
@Named
public class AccountManager {
  public boolean transfer(Account a, Account b) {
    ...
  }
}

Procédure 10.6. Définition et utilisation des stéréotypes

  1. Définir le stéréotype

    @Secure
    @Transactional
    @RequestScoped
    @Named
    @Stereotype
    @Retention(RUNTIME)
    @Target(TYPE)
    public @interface BusinessComponent {
     ...
    }
    
  2. Utilisation d'un stéréotype.

    @BusinessComponent
    public class AccountManager {
      public boolean transfer(Account a, Account b) {
        ...
      }
    }
    
Résultat  :

Les stéréotypes rationalisent et simplifient votre code.

10.2.9. Méthodes Observer

10.2.9.1. Méthodes Observer

Les méthodes Observer reçoivent des notifications quand un événement a lieu.
CDI fournit des méthodes Observer de transactions, qui reçoivent des notifications d'événements avant ou après la phase de complétion de la transaction dans laquelle l'événement a été dirigé.

10.2.9.2. Appliquer et observer des événements

Exemple 10.15. Appliquer un événement

Ce code vous montre un événement injecté et utilisé dans une méthode.
public class AccountManager {
  @Inject Event<Withdrawal> event;
  
  public boolean transfer(Account a, Account b) {
    ...
    event.fire(new Withdrawal(a));
  }
}

Exemple 10.16. Appliquer un événement par un qualificateur

Vous pouvez annoter votre injection d'événement par un qualificateur, pour le rendre plus précis. Pour plus d'informations sur les qualificateurs, voir Section 10.2.3.2, « Qualificateurs ».
public class AccountManager {
  @Inject @Suspicious Event <Withdrawal> event;
  
  public boolean transfer(Account a, Account b) {
    ...
    event.fire(new Withdrawal(a));
  }
}

Exemple 10.17. Observer un événement

Pour observer un événement, utiliser l'annotation @Observes.
public class AccountObserver {
  void checkTran(@Observes Withdrawal w) {
    ...
  }
}

Exemple 10.18. Oberver un événement qualifié

Vous pouvez utiliser des qualificateurs pour observer uniquement certains types d'événements. Pour plus d'informations, voir Section 10.2.3.2, « Qualificateurs »
public class AccountObserver {
  void checkTran(@Observes @Suspicious Withdrawal w) {
    ...
  }
}

10.2.10. Intercepteurs

10.2.10.1. Les intercepteurs

Les intercepteurs font partie de la spécification Enterprise JavaBeans specification, qui se trouve http://jcp.org/aboutJava/communityprocess/final/jsr318/. Les intercepteurs vous permettent d'ajouter des fonctionnalités aux méthodes commerciales d'un bean sans modifier la méthode de bean directement. L'intercepteur est exécuté avant toute autre méthode commerciale du bean.
CDI améliore cette fonctionnalité en vous permettant d'utiliser des annotations pour lier les intercepteurs aux beans.

Points d'interception

Interception de méthodes commerciales
Un intercepteur de méthode commeciale s'applique aux invocations de méthodes du bean par les clients du bean.
Interception de lifecycle callback
Un intercepteur de lifecycle callback s'applique aux invocations de lifecycle callback par le conteneur.
Interception de méthode de timeout
Un intercepteur de méthode de timeout s'applique aux invocations de métodes d'EJB timeout par le conteneur.

10.2.10.2. Utiliser les intercepteurs avec le CDI

Exemple 10.19. Intercepteurs sans le CDI

Sans le CDI, les intercepteurs ont deux problèmes.
  • Le bean doit indiquer l'implémentation de l'intercepteur directement.
  • Chaque bean de l'application doit indiquer l'ensemble des intercepteurs dans l'ordre qui convient. Cela fait que l'ajout ou le retrait d'intercepteurs sur la base d'une application prend beaucoup de temps et comporte un risque d'erreur non négligeable.
@Interceptors({
  SecurityInterceptor.class,
  TransactionInterceptor.class,
  LoggingInterceptor.class
})
@Stateful public class BusinessComponent {
  ...
}

Procédure 10.7. Utiliser les intercepteurs avec le CDI

  1. Définir le type de liaison d'intercepteur.

    @InterceptorBinding
    @Retention(RUNTIME)
    @Target({TYPE, METHOD})
    public @interface Secure {}
    
  2. Marquer l'implémentation de l'intercepteur.

    @Secure
    @Interceptor
    public class SecurityInterceptor {
      @AroundInvoke
      public Object aroundInvoke(InvocationContext ctx) throws Exception {
        // enforce security ...
        return ctx.proceed();
        }
    }
    
  3. Utiliser l'intercepteur dans votre code commercial.

    @Secure
    public class AccountManager {
      public boolean transfer(Account a, Account b) {
        ...
      }
    }
    
  4. Activer l'intercepteur dans votre déploiement, en l'ajoutant à votre fichier META-INF/beans.xml ou WEB-INF/beans.xml.

    <beans>
      <interceptors>
        <class>com.acme.SecurityInterceptor</class>
        <class>com.acme.TransactionInterceptor</class>
      </interceptors>
    </beans>
    Les intercepteurs sont appliqués dans l'ordre indiqué.
Résultat  :

Le CDI simplifie votre code d'intercepteur et simplifie l'application de votre code commercial.

10.2.11. Les décorateurs

Un décorateur intercepte les invocations d'une interface Java particulière. Il est conscient de toutes les sémantiques de cette interface. Les décorateurs sont utiles pour des modèles commerciaux particuliers, mais ne possèdent pas la généralité des intercepteurs. C'est un bean, voire même une classe abstraite, qui implémente le type qu'il décore, et est annoté avec @Decorator.

Exemple 10.20. Exemple de décorateur

@Decorator

public abstract class LargeTransactionDecorator

      implements Account {

   @Inject @Delegate @Any Account account;

   @PersistenceContext EntityManager em;

   public void withdraw(BigDecimal amount) {

      ...

   }
    
   public void deposit(BigDecimal amount);

      ...

   }

}

10.2.12. Extensions portables

CDI doit être une fondation de frameworks, d'extensions et d'intégration à d'autres technologies. De ce fait, CDI expose un ensemble de SPI à utiliser par les développeurs d'extensions portables aux CDI. Les extensions fournissent ces types de fonctionnalités :
  • intégration par les moteurs de Business Process Management
  • intégration aux frameworks de tierce partie comme Spring, Seam, GWT ou Wicket
  • nouvelle technologie basée sur le modèle de programmation CDI
D'après la spécification JSR-299, une extension portable peut s'intégrer avec le conteneur d'une des manières suivantes :
  • En fournissant ainsi ses propres beans, intercepteurs et décorateurs au conteneur
  • En injectant des dépendances dans ses propres objets en utilisant le service d'injections de dépendances
  • En fournissant une implémentation de contexte pour un scope personnalisé
  • En augmentant ou en remplaçant les métadonnées basées annotation par les métadonnées d'autres sources

10.2.13. Proxies Beans

10.2.13.1. Proxys Bean

Un proxy est une sous-classe de bean, qui est générée lors de l'exécution. Il est injecté au moment de la création du bean et les beans scoped dépendants peuvent être injectés, parce que les cycles de vie des beans dépendants sont liés au proxy. Les proxys sont utilisées en tant que substitut pour l'injection de dépendance et résolvent deux problèmes différents.

Problèmes d'injection de dépendance, qui sont résolus en utilisant les proxys.

  • Performance - les proxys sont bien plus rapides que l'injection de dépendance, donc vous pouvez les utiliser dans des beans qui requièrent une haute performance.
  • Thread safety - les proxys envoient des requêtes vers l'instance de bean qui convient, même quand plusieurs threads accèdent à un bean en même temps.

Classes qui ne peuvent pas être mises en proxy

  • Types Primitives ou Tableaux
  • Classes final ou qui possèdent une méthode final
  • Classes qui ont un constructeur non-privé par défaut

10.2.13.2. Utiliser un Proxy ou une Injection

Aperçu

Un proxy est utilisé pour l'injection quand les cycles de vie des beans sont différents les uns des autres. Le proxy est une sous-classe du bean qui est créée au moment de l'exécution et qui remplace toutes les méthodes non privées de la classe de bean. Le serveur proxy transmet l'invocation sur l'instance réelle du bean.

Dans cet exemple, l'instance PaymentProcessor n'est pas injectée directement dans Shop. À la place, un proxy est injecté, et quand la méthode processPayment() est appelée, le proxy cherche l'instance de bean courante PaymentProcessor et appelle la méthode processPayment() dessus.

Exemple 10.21. Injection de proxy

@ConversationScoped
class PaymentProcessor
{
  public void processPayment(int amount)
  {
    System.out.println("I'm taking $" + amount);
  }
}
 
@ApplicationScoped
public class Shop
{
 
  @Inject
  PaymentProcessor paymentProcessor; 
 
  public void buyStuff()
  {
    paymentProcessor.processPayment(100);
  }
}
Pour plus d'informations sur les proxys, y compris les types de classes qui peuvent être mises en proxy, voir Section 10.2.13.1, « Proxys Bean ».

Chapitre 11. Java Transaction API (JTA)

11.1. Aperçu

11.1.1. Java Transactions API (JTA)

Introduction

Ces topics vous donnent des explications de base sur l'API Java Transactions (JTA).

11.2. Concepts de transactions

11.2.1. Transactions

Une transaction se compose de deux ou plusieurs actions qui doivent toutes réussir ou échouer. Le succès est une validation et un échec est un roll-back. Dans un roll-back, l'état de chaque membre retourne à son état d'origine, avant que la transaction ait été commise.
Le standard d'une transaction bien conçue est qu'elle doit être Atomique, Consistante, Isolée, et Durable (ACID).

11.2.2. Les propriétés ACID de transactions

ACID est un acronyme pour Atomicity, Consistency, Isolation, et Durability. Cette terminologie est normalement utilisée dans le contexte de bases de données ou d'opérations transactionnelles.

Définitions ACID

Atomicité
Pour qu'une transaction soit atomique, tous les membres de la transaction doivent prendre la même décision. Ils doivent soit tous valider, soit tous se retirer. Si l'atomicité est cassée, ce qui en résultera s'appelle un heuristic outcome..
Homogénéité
Homogénéité signifie que les données écrites dans la base de données doivent être valides, en terme de schéma de base de données. La base de donnée et les autres sources de données doivent toujours être dans un état consistant. Par exemple, un état inconsistant pourrait correspondre à un champ dans lequel la moitié des données sont écrites avant qu'une opération n'échoue. Dans le cas d'un état consistant, toutes les données sont écrites, et l'écriture est retirée si incomplète.
Isolation
Isolation signifie que les données ajoutées à l'opération doivent être verrouillées avant toute modification, pour ne pas que les processus modifient les données au delà de la portée de la transaction en question.
Durabilité
Durabilité signifie qu'en cas d'échec externe, après que les membres d'une transaction aient été instruits de valider, tous les membres seront disponibles pour continuer la validation de la transaction quand l'échec sera résolu. Cette défaillance est sans doute liée au matériel, logiciels, réseau, ou tout autre système impliqué.

11.2.3. Coordinateur de transactions ou Gestionnaire de transactions

Les termes Coordinateur de transactions et Gestionnaire de transactions sont généralement interchangeables en terme de transaction dans JBoss EAP 6. Le terme Coordinateur de transactions est généralement utilisé dans un contexte de transactions distribuées.
Dans les transactions JTA, le Gestionnaire de transactions s'exécute dans JBoss EAP 6 et communique avec les participants de la transaction pendant le protocole two-phase commit.
Le gestionnaire de transactions ordonne aux participants de commettre ou de retirer leurs données, selon le résultat des autres participants. De cette façon, il s'assure que les transactions respectent la norme ACID.
Pour les transactions JTS, le Coordinateur de transactions gère les interactions entre les gestionnaires de transactions sur des serveurs différents.

11.2.4. Participants à une transaction

Un participant est un processus qui se trouve dans une transaction, ayant la possibilité de valider ou de retirer l'état. Il peut s'agir d'une base de données ou d'une toute autre application. Chaque participant à une transaction décide par lui-même de valider, ou de revenir en arrière sur son état, et c'est seulement si tous les autres participants peuvent valider que la transaction dans son ensemble réussit. Sinon, chacun d'entre eux sera retiré et la transaction n'aura pas lieu. Le gestionnaire de transactions coordonne les opérations de validation ou de renvoi en arrière et il détermine le résultat de la transaction.

11.2.5. JTA (Java Transactions API)

Java Transactions API (JTA) est une spécification pour les transactions à utiliser dans les applications JBoss Enterprise Edition. Définie dans JSR-907.
Les transactions JTA ne sont pas distribuées à travers les serveurs d'applications multiples, et ne peuvent pas être imbriquées.
Les transactions JTA sont contrôlées par le conteneur EJB. Les annotations représentent une méthode pour créer et contrôler des transactions dans votre code.

11.2.6. JTS (Java Transaction Service)

Java Transaction Service (JTS) est un mécanisme servant à soutenir les transactions Java Transaction API (JTA) quand les participants aux transactions se trouvent dans de multiples conteneurs Java Enterprise Edition (serveurs d'application). Tout comme dans les transactions JTA locales, chaque conteneur exécute un procédé appelé Transaction Manager (TM). Les TM communiquent entre eux grâce à un procédé appelé Object Request Broker (ORB), et une communication standard appelée Common Object Request Broker Architecture (CORBA).
Du point de vue d'une application, une transaction JTS fonctionne de la même façon qu'une transaction JTA. La différence est que les participants à la transaction et les sources de données se trouvent dans des conteneurs différents.

Note

L'implémentation de JTS compris dans JBoss EAP 6 prend en charge les distributed JTA transactions. La différence entre les transactions JTA distribuées et les transactions JTS entièrement compatibles est l'interopérabilité avec les ORB de tierce-parties externes. Cette fonctionnalité n'est pas prise en charge dans JBoss EAP 6. Les configurations prises en charge distribuent des transactions à travers des conteneurs JBoss EAP 6 multiples uniquement.

11.2.7. Sources de données XA et transactions XA

Une source de données XA correspond à une source de données qui peut participer à une transaction globale XA.
Une transaction XA est une transaction qui peut s'étendre sur plusieurs ressources. Comprend un gestionnaire de coordination des transactions, avec une ou plusieurs bases de données ou d'autres ressources transactionnelles, toutes impliquées dans une transaction globale unique.

11.2.8. Recouvrement XA

L'API JTA (Java Transaction API) autorise les transactions distribuées sur de multiples X/Open XA resources. XA signifie Extended Architecture développée par le groupe X/Ouvert pour définir une transaction qui utilise plus d'une banque de données principale. La norme XA décrit l'interface entre un Transaction Manager (TM) global et un manager de ressources local. XA autorise de multiples ressources, telles que les serveurs d'application, les bases de données, les caches et les queues de message, à participer à la même transaction, tout en préservant l'atomicité de la transaction. Atomicité signifie que si l'un des participants ne parvient pas à appliquer ses changements, les autres participants metteront fin à la transaction, et restaureront leur état au même statut qu'ils possédaient avant que la transaction n'ait été effectuée.
XA Recovery est le procédé visant à garantir que toutes les ressources affectées par une transaction soient mises à jour ou annulées, même si certaines de ces ressources sont un crash de participants à la transaction ou deviennent indisponibles. Dans le scope de JBoss EAP 6, le sous-système de transaction fournit les mécanismes pour une récupération XA à toute ressource XA ou à tout sous-systèmes qui les utilise, comme les sources de données XA, les queues de messages JMS et les adaptateurs de ressources JCA.
Le recouvrement XA se produit sans l'intervention de l'utilisateur. Dans le cas d'un échec de recouvrement XA, les erreurs sont enregistrées dans la sortie du journal. Veuillez contacter le Service d'assistance globale de Red Hat pour toute demande d'assistance.

11.2.9. Le protocole de validation en 2-Phases

Le protocole de validation en 2 phases (2PC) correspond à un algorithme qui détermine le résultat d'une transaction.
Phase 1

Au cours de la première phase, les participants à la transaction indiquent au coordinateur de transaction s'ils sont en mesure de valider la transaction ou bien, s'ils doivent la retirer.

Phase 2

Dans la deuxième phase, le coordinateur de transactions prend la décision de savoir si l'opération globale doit être validée ou bien, retirée. Si l'un des participants ne peut pas valider, la transaction sera retirée. Sinon, l'opération peut être validée. Le coordinateur dirige les opérations et en notifie le coordinateur quand les décisions sont prises. À ce moment-là, la transaction est terminée.

11.2.10. Les délais d'attente des transactions

Afin de préserver l'atomicité et de respecter la norme ACID des transactions, certaines parties d'une transaction peuvent être longues. Les participants à la transaction doivent verrouiller des pièces de sources de données lorsqu'ils commettent, et le gestionnaire de transactions doit attendre d'avoir des nouvelles de chaque participant à une transaction avant de pouvoir tous les diriger à commettre ou à annuler. Les ressources peuvent être verrouillés indéfiniment pour cause de panne de matériel ou de réseau.
Les délais d'attente de transactions peuvent être associés à des transactions afin de contrôler leur cycle de vie. Si un seuil de délai d'attente passe avant que la transaction s'engage ou soit annulée, le délai d'attente provoque l'annulation de l'opération automatiquement.
Vous pouvez configurer les valeurs de délai d'attente par défaut pour tout le sous-système de la transaction, ou bien, vous pouvez désactiver les valeurs de délai d'attente par défaut et spécifier les délais d'attente sur une base d'une transaction.

11.2.11. Les transactions distribuées

Une transaction distribuée, ou transaction distribuée Java Transaction API (JTA) est une transaction avec des participants sur de multiples serveurs JBoss EAP 6. Les transactions distribuées diffèrent des transactions Java Transaction Service (JTS) du fait que les spécifications JTS requièrent que les transactions puissent être distribuées à travers les serveurs d'application de différents fournisseurs. Le JBoss EAP prend en charge les transactions JTA distribuées.

11.2.12. API de portabilité ORB

L'Object Request Broker (ORB) est un processus qui envoie et reçoit des messages aux participants de la transaction, aux coordinateurs, aux ressources et aux autres services distribués sur plusieurs serveurs d'applications. Un ORB utilise un IDL (Interface Description Language) pour communiquer et interpréter des messages. Common Object Request Broker Architecture (CORBA) est l'IDL utilisé par l'ORB dans JBoss EAP 6.
Le principal type de service qui utilise un ORB est un système de Transactions Java distribuées qui utilise le protocole Java Transaction Service (JTS). Les autres systèmes, notamment les systèmes hérités, peuvent choisir d'utiliser un ORB pour la communication, plutôt que d'autres mécanismes tels que Enterprise JavaBeans à distance ou les services JAX-WS ou JAX-RS Web.
L'API de portabilité ORB fournit des mécanismes pour interagir avec un ORB. Cet API fournit des méthodes pour obtenir une référence à l'ORB, ainsi que pour placer une application dans un mode d'écoute des connexions entrantes de l'ORB. Certaines des méthodes de l'API ne sont pas supportées par tous les ORB. Dans ces cas, une exception sera levée.
Un API comprend plusieurs classes :

Classes API de la portabilité ORB

  • com.arjuna.orbportability.orb
  • com.arjuna.orbportability.oa
Voir les documents JBoss EAP 6 Javadocs du portail Red Hat Customer pour obtenir des détails sur les méthodes et les propriétés incluses dans l'API de portabilité ORB.

11.2.13. Transactions imbriquées

Les transactions imbriquées sont des transactions dans lesquelles les participants sont également des transactions.

Les avantages des transactions imbriquées

Isolation des erreurs
Si une sous-transaction s'annule, par exemple parce qu'un objet utilisé échoue, la transaction englobante ne nécessite pas d'être annulée.
Modularité
Si une transaction est déjà associée à un appel quand une nouvelle transaction commence, la nouvelle transaction sera imbriquée à l'intérieur. Dès lors, si vous savez qu'un objet requiert des transactions, vous pouvez les créer dans l'objet. Si les méthodes de l'objet sont invoquées sans une transaction client, alors les transactions de l'objet sont de niveau supérieur. Sinon, elles sont imbriquées dans l'étendue des transactions du client. De la même manière, un client n'a pas besoin de savoir si un objet est transactionnel. Il peut commencer sa propre transaction.
Les transactions imbriquées sont seulement prises en charge dans le cadre de l'API JTS (Java Transaction Service) et non pas dans le cadre du JTA (Java Transaction API). Le fait de tenter d'imbriquer des transactions JTA (non distribuées) entraîne une exception.

11.3. Optimisations des transactions

11.3.1. Optimisations de transactions

Introduction

Le sous-système de transactions de JBoss EAP 6 inclut plusieurs optimisations dont vous pouvez tirer avantage dans vos applications.

11.3.2. Optimisation LRCO pour une validation en 1 phase (1 PC)

Bien que le protocole de validation en deux phases (2PC) soit plus commun, certaines situations ne peuvent pas accommoder les deux phases. Dans ces cas, vous pouvez utiliser le protocole single phase commit (1PC). Cela a lieu par exemple quand une source de données non-XA-aware doit participer à la transaction.
Dans ces situations, une optimisation appelée Last Resource Commit Optimization (LRCO) est employée. La ressource monophasée est traitée en dernier dans la phase de préparation de la transaction, et il y a une tentative de validation. Si la validation réussit, le journal des transactions est écrit et les ressources restantes passent par le protocole 2PC. Si la dernière ressource ne parvient pas à être validée, la transaction est annulée.
Malgré que ce protocole permet le traitement de la plupart des opérations, certains types d'erreurs peuvent aboutir à des résultats de transactions inconsistants. Il est donc recommandé d'utiliser cette approche en dernier ressort.
Quand on utilise une source de données locale TX dans une transaction, l'optimisation LRCO est automatiquement appliquée.

11.3.2.1. Commit Markable Resource (CRM)

Résumé

La configuration de l'accès à un gestionnaire de ressources via l'interface CRM de validation de ressources marquables (Commit Markable Resource) garantit que le gestionnaire de ressources 1PC puisse être inscrit dans une transaction 2PC. C'est une implémentation de l'algorithme LRCO, qui rend les ressources non-XA entièrement récupérables.

Jusqu'à maintenant, l'ajout de ressources 1PC à une transaction 2PC était réalisée par la méthode LRCO, mais il y a place à erreur avec LRCO. Suivre la procédure ci-dessous pour ajouter des ressources 1PC à une transaction 2PC via la méthode LRCO :
  1. Préparer 2PC
  2. Valider LRCO
  3. Write tx log
  4. Commit 2PC
Si la procédure échoue entre les étapes 2 et 3, vous ne pourrez pas valider le 2PC. CMR élimine cette restriction et permet à 1PC d'être bien inscrit dans une transaction 2PC.
Restrictions

Une transaction peut ne contenir qu'une ressource CMR.

Conditions préalables

Vous devez avoir un tableau créé pour lequel le SQL suivant fonctionne :

SELECT xid,actionuid FROM _tableName_ WHERE transactionManagerID IN (String[])
DELETE FROM _tableName_ WHERE xid IN (byte[[]])
INSERT INTO _tableName_ (xid, transactionManagerID, actionuid) VALUES (byte[],String,byte[])

Exemple 11.1. Voici quelques exemples de requêtes SQL

Sybase :
CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))
Oracle :
CREATE TABLE xids (xid RAW(144), transactionManagerID varchar(64), actionuid RAW(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)
IBM :
CREATE TABLE xids (xid VARCHAR(255), transactionManagerID varchar(64), actionuid VARCHAR(255))
CREATE UNIQUE INDEX index_xid ON xids (xid)
SQL Server :
CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)
Postgres :
CREATE TABLE xids (xid bytea, transactionManagerID varchar(64), actionuid bytea)
CREATE UNIQUE INDEX index_xid ON xids (xid)
Activer un gestionnaire de ressources en tant que CMR

Par défaut, la fonctionnalité CMR est désactivée pour les sources de données. Pour l'activer, vous devez créer ou modifier la configuration de source de données et vous assurer que l'attribut connectable est défini sur true. Voici un exemple d'entrée de configuration de la section sources de données d'un fichier de configuration xml serveur :

<datasource enabled="true" jndi-name="java:jboss/datasources/ConnectableDS" pool-name="ConnectableDS" jta="true" use-java-context="true" spy="false" use-ccm="true" connectable="true"/>

Note

Cette fonctionnalité ne s'applique pas aux sources de données XA.
Vous pouvez également activer un gestionnaire de ressources en tant que CMR par le CLI de la façon suivante :
/subsystem=datasources/data-source=ConnectableDS:add(enabled="true", jndi-name="java:jboss/datasources/ConnectableDS", jta="true", use-java-context="true", spy="false", use-ccm="true", connectable="true", connection-url="validConnectionURL", driver-name="h2")
Mettre à jour une ressource existante pour qu'elle puisse utiliser la nouvelle fonctionnalité CMR

Si vous ne devez mettre à jour qu'une seule ressource existante pour utiliser la nouvelle fonctionnalité CMR, il vous suffira de modifier l'attribut connectable :

/subsystem=datasources/data-source=ConnectableDS:write-attribute(name=connectable,value=true)

Identification des sources de données pouvant utiliser la fonctionnalité CMR

Le sous-système de transactions identifie les sources de données qui peuvent utiliser la fonctionnalité CMR par l'intermédiaire d'une entrée dans la section de config du sous-système de transaction comme dans l'exemple ci-dessous :

<subsystem xmlns="urn:jboss:domain:transactions:3.0">
    ...
    <commit-markable-resources>
        <commit-markable-resource jndi-name="java:jboss/datasources/ConnectableDS">
            <xid-location name="xids" batch-size="100" immediate-cleanup="false"/>
        </commit-markable-resource>
        ...
    </commit-markable-resources>
</subsystem>

Note

Vous devez démarrer le serveur à nouveau pour ajouter le CMR.

11.3.3. Optimisation Presumed-Abort

Quand une transaction est sur le point d'être supprimée, elle peut enregistrer ces informations localement et informer tous les participants inscrits. Cette notification est une courtoisie seulement et n'a aucun effet sur le résultat de la transaction. Après que tous les participants aient été contactés, les informations sur la transaction peuvent être supprimés.
Si une demande subséquente au sujet de l'état de la transaction se produit, il y n'aura aucun information disponible. Dans ce cas, le demandeur suppose que la transaction a avorté et a été restaurée. Cette optimisation presumed-abort indique qu'aucune information sur les participants ne doit être rendue persistante tant que la transaction n'a pas été engagée, car tout échec avant ce point sera considéré comme un abandon de la transaction.

11.3.4. Optimisation Lecture-seule

Lorsqu'un participant est invité à se préparer, il peut indiquer au coordonnateur qu'il n'a pas modifié de données durant la transaction. Une tel participant n'a pas besoin d'être informé de l'issue de l'opération, puisque le sort du participant n'a aucun effet sur la transaction. Ce participant read-only peut être omis de la deuxième phase du protocole de validation.

11.4. Résultats de t