Red Hat Training
A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform
Guide de développement
À utiliser dans Red Hat JBoss Enterprise Application Platform 6
Red Hat Customer Content Services
Résumé
Chapitre 1. Introduction au développement d'applications
1.1. Introduction
1.1.1. Red Hat JBoss Enterprise Application Platform 6
1.2. Conditions préalables
1.2.1. Familiarisez vous avec Java Enterprise Edition 6
1.2.1.1. Les Profils EE 6
1.2.1.2. Web Profil de Java Enterprise Edition 6
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
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
- 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 sous-fichier de configurationmain/
qui contient un fichier de configuration (module.xml
) et tous les fichiers JAR requis. Le nom du module est défini dans le fichiermodule.xml
. Toutes les API fournies 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, à l'excepté du nom de sous-répertoiremain/
.Les modules fournis dans les distributions JBoss EAP se trouvent dans un répertoiresystem
se trouvant lui-même dans le répertoireJBOSS_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épertoiresystem
.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épertoireJBOSS_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épertoiresystem
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'environnementJBOSS_MODULE_PATH
pour changer les emplacements où JBoss EAP cherche les modules, le produit ira chercher dans une structure de sous-répertoiresystem
dans un des emplacements spécifiés. Une structure de sous-répertoiresystem
doit exister quelquepart dans les emplacements spécifiés dansJBOSS_MODULEPATH
. - Modules dynamiques
- Les modules dynamiques sont créés et chargés par le serveur d'applications 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.
1.3. Installer l'environnement de développement
1.3.1. Télécharger et installer Red Hat JBoss Developer Studio
1.3.1.1. Installer Red Hat JBoss Developer Studio
1.3.1.2. Téléchargez Red Hat JBoss Developer Studio
- Visitez https://access.redhat.com/.
- Sélectionnez Téléchargements du menu en haut de la page.
- Cherchez
Red Hat JBoss Developer Studio
dans la liste et cliquez dessus. - Sélectionnez la version appropriée et cliquez sur Télécharger.
1.3.1.3. Installer Red Hat JBoss Developer Studio
- Conditions préalables :
Procédure 1.1. Installer Red Hat JBoss Developer Studio
- Ouvrir un terminal.
- Aller dans le répertoire qui contient le fichier téléchargé
.jar
. - Exécuter la commande suivante pour lancer le GUI d'installation.
java -jar jbdevstudio-build_version.jar
- Cliquer sur Suivant pour commencer le processus d'installation.
- Sélectionner I accept the terms of this license agreement (J'accepte les conditions de licence) et cliquer sur Suivant.
- Ajuster le chemin d'accès de l'installation et cliquer sur Suivant.
Note
Si le dossier de chemin d'installation n'existe pas, vous verrez une invite. Cliquer alors sur Ok pour créer le dossier. - Choisir une JVM, ou bien conserver la JVM sélectionnée par défaut, et cliquer sur Suivant.
- Ajouter une plateforme d'applications disponible, et cliquer sur Suivant.
- Vérifier les informations d'installation et cliquer sur Suivant.
- Cliquer sur Suivant une fois le processus d'installation terminé.
- Configurer les raccourcis bureau pour Red Hat JBoss Developer Studio et cliquer sur Next (suivant).
- Cliquer sur le bouton Done (Terminé).
1.3.1.4. Démarrer Red Hat JBoss Developer Studio
- Conditions préalables :
Procédure 1.2. Commande de démarrage de Red Hat JBoss Developer Studio
- Ouvrir un terminal.
- Aller dans le répertoire d'installation.
- 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
Procédure 1.3. Ajouter le serveur
- Ouvrir l'onglet Serveurs. S'il n'y a pas d'onglet Serveurs, l'ajouter au panneau comme suit :
- Cliquer sur Window → Show View → Other....
- Sélectionner Servers à partir du dossier Server et cliquer sur OK.
- Cliquer sur le lien No servers are available. Click this link to create a new server... (Aucun serveur disponible. Cliquer sur ce lien pour créer un nouveau serveur...) ou bien, cliquer à droite dans le panneau vide Serveur, et sélectionner New → Server.
Figure 1.1. Ajouter un nouveau serveur - Aucun serveur disponible
- Étendre JBoss Enterprise Middleware et choisir JBoss Enterprise Application Platform 6.1+. Saisir un nom de serveur, comme par exemple "JBoss Enterprise Application Platform 6.4", 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.
Figure 1.2. Définir un nouveau serveur
- Créer un Adaptateur de serveur pour gérer le démarrage et l'arrêt du serveur. Conserver les valeurs par défaut, et cliquer sur Next.
Figure 1.3. Créer un nouvel Adpatateur de serveur
- Saisir un nom comme "JBoss EAP 6.4 Runtime". Sous Home Directory, cliquer sur Browse puis, naviguer vers l'emplacement de l'installation de JBoss EAP. Puis, cliquer sur Next.
Figure 1.4. Ajouter un environnement de runtime du nouveau serveur
Note
Certains quickstarts exigent que vous exécutiez le serveur avec un profil différent ou avec des arguments supplémentaires. Pour déployer un quickstart qui nécessite le profilfull
, vous devez définir un nouveau serveur et ajouter un Server Runtime Environment qui spécifiestandalone-full.xml
dans le Configuration file. Veillez à bien donner un nom descriptif au nouveau serveur. - Vous permet de configurer les projets existants pour le nouveau serveur. Puisque vous n'avez pas de projet à ce stade, cliquer sur Finish.
Figure 1.5. Modifier les ressources dans le nouveau serveur de JBoss
Le serveur de JBoss EAP est listé dans l'onglet Servers.
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
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
- Maven 3.0.0 ou versions plus récentes. Pour en savoir plus sur l'installation de Maven, veuillez consulter http://maven.apache.org/download.html.
- Le référentiel JBoss EAP 6 Maven est disponible en ligne, donc il n'est pas utile de le télécharger ou de l'installer localement. Si vous envisagez de l'utiliser en ligne, passer à l'étape suivante. Si vous préférez l'installer dans un référentiel local, consulter : Section 2.2.3, « Installer le référentiel Maven de JBoss EAP 6 localement ».
Procédure 1.4. Télécharger les quickstarts
- Ouvrir un navigateur web, et accéder à cet URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Trouver "Quickstarts" dans la liste.
- Cliquer sur le bouton Télécharger pour télécharger un fichier ZIP contenant les exemples.
- Décompressez l'archive dans un répertoire de votre choix.
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
Important
- Si vous ne l'avez pas encore fait, Section 2.3.2, « Configurer le référentiel JBoss EAP 6 Platform Maven Repository par les paramètres de configuration de Maven ».
- Démarrer Red Hat JBoss Developer Studio.
- À partir du menu, sélectionner Fichier → Importer.
- Dans la liste sélectionnée, choisir Maven → Projets Maven existants, puis cliquer sur Suivant.
Figure 1.7. Importer les projets Maven existants
- 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 fichierpom.xml
du projet quickstart sélectionné.Figure 1.8. Sélectionner les projets Maven
- Cliquer sur Terminé.
Procédure 1.6. Générer et déployer le Quickstart helloworld
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.
- Si vous ne voyez pas l'onglet Servers ou si vous n'avez pas encore défini de serveur, suivre les instructions suivantes : Section 1.3.1.5, « Ajouter le serveur de JBoss EAP en utilisant Define New Server ». Si vous avez l'intention de déployer un quickstart qui requiert un profil
full
et des arguments de démarrage supplémentaires, veillez bien à créer un environnement de runtime selon les instructions de quickstart. - 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.Figure 1.9. Run As - Run on Server
- Sélectionner JBoss EAP 6.1+ Runtime Server de la liste de serveurs, et cliquer sur Next.
Figure 1.10. Exécuter sur le serveur
- 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.Figure 1.11. Modifier les ressources configurées sur le serveur
- Vérifier les résultats.
- Dans l'onglet
Server
, le statut de JBoss EAP 6.x Runtime Server passe à[Started, Republish]
. - L'onglet Console du serveur affiche des messages détaillant le démarrage du serveur JBoss EAP 6.x 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.
- 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
Procédure 1.7. Exécuter les tests Arquillian du quickstart bean-validation
bean-validation
est un exemple de quickstart qui fournit des tests Arquillian.
- Suivre la procédure ci-dessus pour importer le quickstart
bean-validation
dans le Red Hat JBoss Developer Studio. - Dans l'onglet Servers, cliquer à droite dans le serveur et sélectionner le bouton Start pour démarrer le serveur JBoss EAP. Si vous n'apercevez pas l'onglet Servers et ou si vous n'avez pas encore défini de serveur, suivre les instructions suivantes : Section 1.3.1.5, « Ajouter le serveur de JBoss EAP en utilisant Define New Server ».
- Cliquer à droite sur le projet
jboss-bean-validation
qui se trouve dans l'onglet Project Explorer et sélectionner Run As. On vous présentera une liste d'options. Sélectionner Maven Build. - Dans le champ d'entrée Goals du dialogue Edit Configuration, saisir :
clean test -Parq-jbossas-remote
Puis cliquer sur Run.Figure 1.12. Modifier la configuration
- Vérifier les résultats.L'onglet de serveur Console affiche des messages donnant des informations sur le démarrage du serveur JBoss EAP et la sortie des tests Arquillian de quickstart
bean-validation
.------------------------------------------------------- T E S T S ------------------------------------------------------- Running org.jboss.as.quickstarts.bean_validation.test.MemberValidationTest Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.189 sec Results : Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
1.4.2.2. Exécuter les quickstarts par la Ligne de commande
Procédure 1.8. Générer et déployer les quickstarts par la Ligne de commande
- Si vous ne l'avez pas encore fait, Section 2.3.2, « Configurer le référentiel JBoss EAP 6 Platform Maven Repository par les paramètres de configuration de Maven ».
- 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 prérequis 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 colonnePrérequis
. Si un quickstart est associé à une liste de prérequis, 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. - Exécuter le quickstart
helloworld
Le quickstarthelloworld
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 fichierREADME.html
dans la racine du quickstarthelloworld
. Il contient des instructions détaillées sur la façon de construire et de déployer le quickstart et accéder à l'application en cours. - 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
Le Quickstart helloworld vous montre comment déployer un simple Servlet dans la plateforme JBoss EAP 6. La logique métier 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.
helloworld
.
- Installer Red Hat JBoss Developer Studio en suivant la procédure qui suit : Section 1.3.1.3, « Installer Red Hat JBoss Developer Studio ».
- Configurer Maven pour qu'il puisse être utilisé avec Red Hat JBoss Developer Studio en suivant la procédure qui suit : Section 2.3.3, « Configurer Maven pour utilisation dans Red Hat JBoss Developer Studio ».
- Suivre les procédures suivantes pour importer, créer et déployer le Quickstart
helloworld
dans Red Hat JBoss Developer Studio : Section 1.4.2.1, « Exécuter les quickstarts (démarrages rapides) dans Red Hat JBoss Developer Studio » - Vérifier que le Quickstart
helloworld
est déployé correctement dans JBoss EAP en ouvrant un navigateur web, et en accédant à l'application dans cet URL : http://localhost:8080/jboss-helloworld
Procédure 1.9. Observer la structure du 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.
- Le fichier
beans.xml
se trouve dans le dossierWEB-INF/
qui se trouve dans le répertoiresrc/main/webapp/
du Quickstart. - Le répertoire
src/main/webapp/
inclut également un fichierindex.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. - Tous les fichiers de configuration de cet exemple se trouvent dans
WEB-INF/
, qui se trouve dans le répertoiresrc/main/webapp/
de l'exemple. - Notez que le Quickstart n'a pas même besoin d'un fichier
web.xml
!
Procédure 1.10. Examiner le code
Vérifier le code HelloWorldServlet
Le fichierHelloWorldServlet.java
se trouve dans le répertoiresrc/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 pas 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. Vérifier le code HelloService
Le fichierHelloService.java
se trouve dans le répertoiresrc/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
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.
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.
numberguess
.
- Installer Red Hat JBoss Developer Studio en suivant la procédure qui suit : Section 1.3.1.3, « Installer Red Hat JBoss Developer Studio ».
- Configurer Maven pour qu'il puisse être utilisé avec Red Hat JBoss Developer Studio en suivant la procédure qui suit : Section 2.3.3, « Configurer Maven pour utilisation dans Red Hat JBoss Developer Studio ».
- Suivre les procédures suivantes pour importer, créer et déployer le Quickstart
numberguess
dans Red Hat JBoss Developer Studio : Section 1.4.2.1, « Exécuter les quickstarts (démarrages rapides) dans Red Hat JBoss Developer Studio » - Vérifier que le Quickstart
numberguess
est déployé correctement dans JBoss EAP en ouvrant un navigateur web, et en accédant à l'application dans cet URL : http://localhost:8080/jboss-numberguess
Procédure 1.11. Examiner les fichiers de configuration
WEB-INF/
, qui se trouvent dans le répertoire src/main/webapp/
du Quickstart.
- Examiner le fichier
faces-config.xml
.Ce Quickstart utilise la version de JSF 2.0 defaces-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>
- Examiner le fichier
beans.xml
.Il y a également un fichierbeans.xml
qui indique à JBoss EAP 6 de chercher des beans dans cette application et d'activer le CDI. - Il n'y a pas de fichier
web.xml
Notez que le Quickstart n'a pas même besoin d'un fichierweb.xml
!
Procédure 1.12. Examiner le code JSF
.xhtml
pour les fichiers source, mais s'occupe des vues rendues par l'extension .jsf
.
- Examiner le code
home.xhtml
.Le fichierhome.xhtml
se trouve dans le répertoiresrc/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 Remarque 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.13. Examiner les fichiers de classe
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.
- Vérifier le code du qualificateur
Random.java
.Un qualificateur est utilisé pour supprimer l'ambiguïté entre deux beans, qui sont tous deux candidats 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 ambigue. »Le qualificateur@Random
est utilisé pour injecter un nombre au hasard.@Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented @Qualifier public @interface Random { }
- Vérifier le code du qualificateur
MaxNumber.java
.Lequalificateur
@MaxNumber
est utilisé pour injecter le nombre maximum autorisé.@Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented @Qualifier public @interface MaxNumber { }
- Vérifier le code
Generator.java
.La classeGenerator
est chargée 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, 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; } }
- Vérifier le code
Game.java
.La classe session scopedGame
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 unFacesMessage
. 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
Procédure 1.14. Remplacer l'application web Welcome par défaut par votre propre application web
Désactiver l'application Welcome
Utiliser le script d'interface CLIEAP_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)
Configurer votre application web par le contexte root.
Afin de configurer votre application web, pour qu'elle utilise (/) comme adresse URL, modifier son fichierjboss-web.xml
, qui se trouve dans le répertoireMETA-INF/
ouWEB-INF/
. Remplacer sa directive<context-root>
par une autre directive qui ressemble à ce qui suit.<jboss-web> <context-root>/</context-root> </jboss-web>
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 surhttp://SERVER_URL:PORT/
.
Chapitre 2. Guide Maven
2.1. Pour en savoir plus sur Maven
2.1.1. Le référentiel Maven
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.
2.1.2. Le fichier POM Maven
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.
pom.xml
est disponible sur le lien http://maven.apache.org/maven-v4_0_0.xsd.
2.1.3. Conditions minimum pour un fichier POM Maven
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é
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
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.
settings.xml
.
- Dans l'installation Maven
- Le fichier de configuration se trouve dans le répertoire
M2_HOME/conf/
. On les appelle les paramètresglobaux
. 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 fichierssettings.xml
et Maven existent tous les deux, leurs contenus seront fusionnés. S'il y a une intersection, le fichier utilisateursettings.xml
aura priorité.
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.4-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.4-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>
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
- Aller dans Apache Maven Project - Download Maven et télécharger la dernière distribution de votre système d'exploitation.
- 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
2.2.3. Installer le référentiel Maven de JBoss EAP 6 localement
Le référentiel de JBoss EAP 6 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.
- Ouvrir un navigateur web, et accéder à cet URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Chercher "Red Hat JBoss Enterprise Application Platform VERSION Maven Repository" dans la liste.
- Cliquer sur le bouton Télécharger pour télécharger un fichier
.zip
contenant le référentiel. - Décompresser le fichier sur votre système de fichiers local dans un répertoire de votre choix.
Ceci crée un répertoire de référentiel Maven appelé jboss-eap-version-maven-repository
.
Important
settings.xml
de Maven. Chaque référentiel local doit être configuré dans sa propre balise <repository>
.
Important
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 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
- Ouvrir un navigateur web, et accéder à cet URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Chercher "Red Hat JBoss Enterprise Application Platform <VERSION> Maven Repository" dans la liste.
- Cliquer sur le bouton Télécharger pour télécharger un fichier
.zip
contenant le référentiel. - Décompresser les fichiers dans un répertoire qui soit accessible au serveur Apache.
- Configurer Apache pour lui donner la permission écriture et pour permettre la navigation dans le répertoire créé.
Cela permet à un environnement multi-utilisateurs d'accéder à un référentiel Maven sur Apache httpd.
Note
2.2.5. Installer le référentiel Maven de JBoss EAP 6 en utilisant le gestionnaire de référentiels Nexus Maven
Procédure 2.3. Télécharger l'archive ZIP du référentiel Maven de JBoss EAP 6
- Ouvrir un navigateur web, et accéder à cet URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Chercher "Red Hat JBoss Enterprise Application Platform <VERSION> Maven Repository" dans la liste.
- Cliquer sur le bouton Télécharger pour télécharger un fichier
.zip
contenant le référentiel. - 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
- Connectez-vous à Nexus en tant qu'administrateur.
- Sélectionner la section Référentiels à partir du menu Vues → Référentiels qui se trouve à gauche du gestionnaire de référentiels.
- Cliquer sur le menu déroulant Ajouter..., puis sélectionner Référentiel hébergé.
- Donner un nom et une ID au nouveau référentiel.
- Saisir le chemin du disque qui mène au référentiel décompressé dans le champ d'emplacement Remplacement du stockage local.
- 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.
- Sélectionner le groupe de référentiels.
- Cliquer sur l'onglet Configurer.
- Déplacez le nouveau référentiel JBoss Maven de la liste Référentiels disponibles vers la liste Référentiels de groupes ordonnancés sur la gauche.
Note
Notez que l'ordre de cette liste détermine la priorité de recherche d'artefacts Maven.
Le référentiel est configuré par le gestionnaire de référentiels Maven Nexus.
2.2.6. Gestionnaires de référentiels Maven
- 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.
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
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
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.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 aveccentral
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
- 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.
Note
- 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
Procédure 2.6. Configurer Maven par les paramètres fournis dans les exemples Quickstart
settings.xml
configuré pour utiliser le référentiel JBoss EAP 6 Maven en ligne. Il s'agit de l'approche la plus simple.
- Cette procédure remplace le fichier de configuration Maven existant, donc vous devrez sauvegarder le fichier
settings.xml
Maven existant.- 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\
- 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.
- 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 »
- Copier le fichier
QUICKSTART_HOME/settings.xml
dans le répertoireUSER_HOME/.m2/
. - 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
- 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\
- Si vous ne trouvez pas de fichier
settings.xml
, copier le fichiersettings.xml
du répertoireUSER_HOME/.m2/conf/
dans le répertoireUSER_HOME/.m2/
. - 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>
desettings.xml
.<activeProfile>jboss-ga-repository</activeProfile> <activeProfile>jboss-earlyaccess-repository</activeProfile>
- 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
- 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\
- Si vous ne trouvez pas de fichier
settings.xml
, copier le fichiersettings.xml
du répertoireUSER_HOME/.m2/conf/
dans le répertoireUSER_HOME/.m2/
. - Copier l'XML suivant dans l'élément
<profiles>
du fichiersettings.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>
desettings.xml
.<activeProfile>jboss-eap-repository</activeProfile>
- 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
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.
- À partir du menu, sélectionner Window → Preferences.
- Dans la fenêtre Window Preferences, étendre Maven et sélectionner User Settings.
- Cliquer sur le bouton Update Settings (Mise à jour Configuration) pour réactualiser les configurations utilisateur de Maven dans Red Hat JBoss Developer Studio.
Figure 2.1. Mise à jour des paramètres de configuration de l'utilisateur Maven
Important
- Missing artifact ARTIFACT_NAME
- [ERROR] Failed to execute goal on project PROJECT_NAME; Could not resolve dependencies for PROJECT_NAME
~/.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
Procédure 2.10. Configurer Maven dans Red Hat JBoss Developer Studio
- Cliquer sur Window→Preferences, puis JBoss Tools et sélectionner JBoss Maven Integration.
Figure 2.2. Panneau d'intégration de JBoss Maven dans la fenêtre Préférences
- Cliquer sur Configure Maven Repositories.
- 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 :- Définir les valeurs de Profile ID, Repository ID, et Repository Name à
jboss-ga-repository
. - Définir la valeur de Repository URL à
http://maven.repository.redhat.com/techpreview/all
. - Cliquer sur la case Active by default pour activer le référentiel Maven.
- Cliquer sur OK
Figure 2.3. Ajouter le référentiel Maven - JBoss Tech Preview
- Cliquer sur Add Repository pour configurer le référentiel JBoss Early Access Maven. Remplir les champs de
Add Maven Repository
comme suit :- Définir les valeurs de Profile ID, Repository ID, et Repository Name à
jboss-earlyaccess-repository
. - Définir la valeur de Repository URL à
http://maven.repository.redhat.com/techpreview/all
. - Cliquer sur la case Active by default pour activer le référentiel Maven.
- Cliquer sur OK
Figure 2.4. Ajouter le référentiel Maven - JBoss Early Access
- Vérifier les référentiels et cliquer sur Finish.
Figure 2.5. Vérifier les référentiels Maven
- 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. Clqiuer 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
- Vous pouvez modifier les paramètres Maven.
- Vous pouvez configurer le fichier POM du projet.
pom.xml
. Cette méthode de configuration remplace les configurations Utilisateur et Globales.
Note
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.
Note
- 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
- Ouvrir le fichier
pom.xml
de votre projet dans un éditeur de texte. - 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.x.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>
- 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.x.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
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.
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
fournie
. 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
-redhat
, par exemple 1.0.0-redhat-1
.
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.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifact> <version>4.2.16.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
<dependencyManagement> <dependencies> ... <dependency> <groupId>org.jboss.bom</groupId> <artifactId>eap6-supported-artifacts</artifactId> <version>6.4.0.GA</version> <type>pom</type> <scope>import</scope> </dependency> ... </dependencies> </dependencyManagement>
JBoss JavaEE Specs Bom de nomenclature
jboss-javaee-6.0
contient les JAR de spécification JAVA EE utilisés dans JBoss EAP.
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
Tableau 2.1. Nomenclatures JBoss BOM utilisées par les Quickstarts
IdArté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. |
6.4.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.4.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
jboss-as-ejb-client-bom
et jboss-as-jms-client-bom
.
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.5.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>
2.4. Mise à niveau du référentiel Maven
2.4.1. Appliquer un correctif dans le répertoire Maven local
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.
unzip
.
Conditions préalables
- Accès valide et abonnement au portail clients de Red Hat.
- Le fichier Red Hat JBoss Enterprise Application Platform Maven Repository ZIP <VERSION>, téléchargé et installé localement.
Procédure 2.11. Mise à jour du référentiel Maven
- Ouvrir un navigateur et connectez-vous dans https://access.redhat.com.
- Sélectionnez Téléchargements du menu en haut de la page.
- Cherchez
Red Hat JBoss Enterprise Application Platform
dans la liste et cliquez dessus. - Sélectionner la version de JBoss EAP qui convient à partir du menu déroulant Version qui apparaît sur l'écran, puis cliquer sur Correctifs.
- Chercher
Red Hat JBoss Enterprise Application Platform<VERSION>CPx Incremental Maven Repository
dans la liste et cliquer sur Download. - Vous êtes invité à sauvegarder le fichier ZIP dans un répertoire de votre choix. Sélectionner un répertoire et sauvegarder le fichier.
- 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 ».
- Décompresser le fichier de correctifs Maven dans le répertoire d'installation de JBoss EAP <VERSION>.x.
- Dans Linux, ouvrir un terminal et saisir la commande suivante :
[standalone@localhost:9999 /]
unzip -o jboss-eap-<VERSION>.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épertoire
EAP_MAVEN_REPOSITORY_PATH
.
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
3.1.2. Chargement des classes
3.1.3. Modules
- 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 sous-fichier de configurationmain/
qui contient un fichier de configuration (module.xml
) et tous les fichiers JAR requis. Le nom du module est défini dans le fichiermodule.xml
. Toutes les API fournies 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, à l'excepté du nom de sous-répertoiremain/
.Les modules fournis dans les distributions JBoss EAP se trouvent dans un répertoiresystem
se trouvant lui-même dans le répertoireJBOSS_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épertoiresystem
.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épertoireJBOSS_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épertoiresystem
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'environnementJBOSS_MODULE_PATH
pour changer les emplacements où JBoss EAP cherche les modules, le produit ira chercher dans une structure de sous-répertoiresystem
dans un des emplacements spécifiés. Une structure de sous-répertoiresystem
doit exister quelquepart dans les emplacements spécifiés dansJBOSS_MODULEPATH
. - Modules dynamiques
- Les modules dynamiques sont créés et chargés par le serveur d'applications 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.
3.1.4. Les dépendances de modules
Exemple 3.2. Les dépendances de module
- 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
- 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épertoireWEB-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 :
- Le répertoire
lib/
du EAR est un simple module nommé le module parent. - Chaque déploiement WAR du EAR est un simple module.
- 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
- 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.9.1, « Dépendances de modules implicites » pour obtenir des détails sur chaque dépendance implicite.
- Dépendances explicitesIl 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 JBossjboss-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. - Ressources locales.Fichiers de classe empaquetées à l'intérieur du déploiement lui-même, par ex. les répertoires
WEB-INF/classes
ouWEB-INF/lib
d'un fichier WAR. - 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
- Les déploiements des fichiers WAR et JAR sont nommés selon le format suivant :
deployment.DEPLOYMENT_NAME
Par exemple,inventory.war
etstore.jar
auront les mêmes noms de module quedeployment.inventory.war
etdeployment.store.jar
respectivement. - Les sous-déploiements des archives Enterprise sont nommés selon le format suivant :
deployment.EAR_NAME.SUBDEPLOYMENT_NAME
Ainsi, le sous-déploiementreports.war
, qui se trouve dans l'archive Enterpriseaccounts.ear
, aura le nom de module dudeployment.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 de chargement des classes dans le déploiement.
EAP_HOME/docs/schema/jboss-deployment-structure-1_2.xsd
3.2. Ajouter une dépendance de module explicite à un déploiement
Conditions préalables
- Vous devez déjà avoir un projet de logiciel qui fonctionne, et auquel vous souhaitez ajouter une dépendance de module.
- Vous devez connaître le nom du module qui est ajouté comme dépendance. Voir Section 3.9.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.
- Par l'ajout d'entrées dans le fichier
MANIFEST.MF
du déploiement. - 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
MANIFEST.MF
. Voir Section 3.3, « Générer des entrées MANIFEST.MF en utilisant Maven ».
Ajouter le fichier
MANIFEST.MF
Si le projet ne possède pas de fichierMANIFEST.MF
, créer un fichier nomméMANIFEST.MF
. Pour une application web (WAR), ajouter ce fichier au répertoireMETA-INF
. Pour une archive EJB (JAR), l'ajouter au répertoireMETA-INF
.Ajouter une entrée de dépendance
Ajouter une entrée de dépendance au fichierMANIFEST.MF
avec une liste de noms de modules de dépendance séparés par des virgules.Dépendances : org.javassist, org.apache.velocity
Option : rendre une dépendance optionnelle
On peut rendre une dépendance optionnelle an ajoutantoptional
au nom du module de l'entrée de dépendance.Dépendances : org.javassist optional, org.apache.velocity
Option : export d'une dépendance
On peut exporter une dépendance en ajoutantexport
au nom du module de l'entrée de dépendance.Dépendences : org.javassist, org.apache.velocity export
En option : dépendances utilisant des annotations
Cet indicateur est requis quand la dépendance de module contient des annotations qui doivent être traitées pendant l'analyse des annotations, comme lors de la déclaration des intercepteurs EJB. Si ce n'est pas fait, un intercepteur EJB déclaré dans un module ne peut pas être utilisé en déploiement. Il y a d'autres situations impliquant des analyses d'annotation quand c'est nécessaire.Utiliser cet indicateur exige que le nodule comprenne un index Jandex. Les directives de création et d'utilisation d'un index Jandex sont incluses à la fin de cette section.
Procédure 3.2. Ajouter une configuration de dépendance à jboss-deployment-structure.xml
Ajouter
jboss-deployment-structure.xml
Si l'application n'a pas de fichierjboss-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épertoireWEB-INF
. Pour une archive EJB (JAR), l'ajouter au répertoireMETA-INF
.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.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'attributname
au nom du module.<module name="org.javassist" />
Option : rendre une dépendance optionnelle
On peut rendre une dépendance optionnelle en ajoutant l'attributoptional
à l'entrée du module, avec la valeurtrue
. La valeur par défaut de cet attribut estfalse
.<module name="org.javassist" optional="true" />
Option : export d'une dépendance
On peut exporter une dépendance en ajoutant l'attributoptional
à l'entrée du module, avec la valeurtrue
. La valeur par défaut de cet attribut estfalse
.<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>
L'indicateur d'annotations
exige que le module contienne un index Jandex. Vous pouvez créer un nouvel "index JAR" à ajouter au module. Utiliser le JAR Jandex pour créer l'index, puis l'insérer dans un nouveau fichier JAR :
Procédure 3.3.
Créer l'index
java -jar $JBOSS_HOME/modules/org/jboss/jandex/main/jandex-1.0.3.Final-redhat-1.jar $JAR_FILE
Créer un espace de travail temporaire
mkdir /tmp/META-INF
Déplacer le fichier index sur le répertoire de travail
mv $JAR_FILE.ifx /tmp/META-INF/jandex.idx
- Option 1: Inclure l'index dans un nouveau fichier JAR
jar cf index.jar -C /tmp META-INF/jandex.idx
Puis, metttez le JAR dans le répertoire de modules et modifiermodule.xml
pour y ajouter les roots de ressources. - Option 2: Ajouter l'index à une JAR existante
java -jar $JBOSS_HOME/modules/org/jboss/jandex/main/jandex-1.0.3.Final-redhat-1.jar -m $JAR_FILE
Indiquer à l'importation d'index d'utiliser l'index d'annotations
Indiquer à l'importation d'index d'utiliser l'index d'annotations, pour que le balayage des annotations permettent de les trouver.Choisissez une des méthodes ci-dessous, selon votre situation :- Si vous ajoutez une dépendance de module par MANIFEST.MF, ajouter
annotations
à la suite du nom du module.Ainsi, changer :Dependencies: test.module, other.module
enDependencies: test.module annotations, other.module
- Si vous ajoutez une dépendance de module en utilisant
jboss-deployment-structure.xml
ajouterannotations="true"
à la dépendance du module.
3.3. Générer des entrées MANIFEST.MF en utilisant Maven
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
- Vous devez déjà posséder un projet Maven en cours.
- Le projet Maven doit utiliser l'un des plug-ins JAR, EJB, ou WAR (
maven-jar-plugin
,maven-ejb-plugin
,maven-war-plugin
). - Vous devez connaître le nom des dépendances de module du projet. Voir Section 3.9.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.4. Générer un fichier MANIFEST.MF contenant des dépendances de module.
Ajouter une configuration
Ajouter la configuration suivante à la configuration du plug-in de l'empaquetage dans le fichierpom.xml
du projet.<configuration> <archive> <manifestEntries> <Dependencies></Dependencies> </manifestEntries> </archive> </configuration>
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 leMANIFEST.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>
Build de projet
Procédez au build de projet en utilisant l'objectif d'assemblage de Maven.[Localhost ]$ mvn assembly:assembly
MANIFEST.MF
avec les dépendances de module spécifiées.
Exemple 3.4. Dépendances de module configurées dans pom.xml
<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
Conditions préalables
- Vous devez déjà avoir un projet informatique qui fonctionne et dont vous souhaitez exclure une dépendance.
- Vous devez connaître le nom du module à exclure. Voir Section 3.9.1, « Dépendances de modules implicites » pour obtenir une liste des dépendances implicites et de leurs conditions.
Procédure 3.5. Ajouter une configuration d'exclusion de dépendances de jboss-deployment-structure.xml
- 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épertoireWEB-INF
. Pour une archive EJB (JAR), l'ajouter au répertoireMETA-INF
. - Créer un élément
<deployment>
dans la racine de l'élément et un<exclusions>
à l'intérieur.<deployment> <exclusions> </exclusions> </deployment>
- Dans l'élément d'exclusion, ajouter un élément
<module>
pour chaque module à exclure. Définir l'attributname
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
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.6. Exclure un sous-système
- Ouvrir le fichier
jboss-deployment-structure.xml
dans un éditeur de texte. - Ajouter le XML suivant dans les balises de <deployment> :
<exclude-subsystems> <subsystem name="SUBSYSTEM_NAME" /> </exclude-subsystems>
- Enregistrer le fichier
jboss-deployment-structure.xml
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="jaxrs" /> </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.
- 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.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- 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.
CurrentClass
, vous pourrez obtenir le chargeur de classes de la classe par la méthodeCurrentClass.class.getClassLoader()
.L'exemple suivant donne un exemple de chargeur de classes qui puisse charger et initialiser la classeTargetClass
: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 objetsURL
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éthodeopenStream()
.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'utiliseropenConnection()
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 :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.10. Charger un fichier de classe déjà chargée.
InputStream inputStream = CurrentClass.class.getResourceAsStream(TargetClass.class.getSimpleName() + ".class");
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.
MANIFEST.MF
:
Dépendences: org.jboss.modulesIl 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.
- 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épertoirebin/
, récursivement dans les sous-répertoires.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.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);
- 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éthodeModule.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éthodePathFilters.filtered()
peut fournir une vue filtrée d'un itérateur de ressources dans ce cas. La classePathFilters
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)
- 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.
- 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
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
3.7.3. Désactiver l'isolement du chargeur de classes d'un sous-déploiement dans une EAR
Important
Ajouter le fichier du descripteur de déploiement
Ajouter le fichier de descripteur de déploiementjboss-deployment-structure.xml
au répertoireMETA-INF
de l'EAR s'il n'existe pas déjà et ajouter le contenu suivant :<jboss-deployment-structure> </jboss-deployment-structure>
Ajouter l'élément
<ear-subdeployments-isolated>
Ajouter l'élément<ear-subdeployments-isolated>
au fichierjboss-deployment-structure.xml
s'il n'existe pas déjà dans le contenu defalse
.<ear-subdeployments-isolated>false</ear-subdeployments-isolated>
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 de l'EAR auront des dépendances automatiques sur chacun des sous-déploiements non-WAR.
3.8. Déployer des TLD (Tag Library Descriptors) dans un module personnalisé
Si vous avez plusieurs applications qui utilisent des TLD (Tag Library Descriptors), il peut être utile de séparer les TLD des applications pour qu'ils soient situés à un seul et unique emplacement. Cela permet des additions et mises à jour de TLD facilitées sans devoir nécessairement mettre à jour chaque application qui les utilise.
- Au moins un JAR contenant des TLD. Assurez-vous que les TLD sont empaquetés dans
META-INF
.
Procédure 3.7. Déployer des TLD dans un module personnalisé
- À l'aide du Management CLI, connectez-vous à votre instance JBoss EAP 6 et exécutez la commande suivante pour créer le module personnalisé contenant le JAR TLD :
module add --name=MyTagLibs --resources=/path/to/TLDarchive.jar
Si les TLD sont empaquetés avec des classes qui nécessitent des dépendances, utilisez l'option--dependencies=DEPENDENCY
pour assurer que vous spécifiez ces dépendances lors de la création d'un module personnalisé.Lors de la création du module, vous pouvez spécifier de multiples ressources JAR en les séparant par:
. Par exemple,--resources=/path/to/one.jar:/path/to/two.jar
- Dans vos applications, déclarez une dépendance sur le nouveau module personnalisé MyTagLibs en utilisant l'une des méthodes décrites dans Section 3.2, « Ajouter une dépendance de module explicite à un déploiement ».
Important
Assurez-vous également d'importerMETA-INF
lorsque vous déclarez la dépendance. Par exemple, pourMANIFEST.MF
:Dependencies: com.MyTagLibs meta-inf
Ou, pourjboss-deployment-structure.xml
, utilisez l'attributmeta-inf
.
Dans vos applications, vous pouvez utiliser des TLD contenues dans le nouveau module personnalisé.
3.9. Référence
3.9.1. Dépendances de modules implicites
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 |
| | |
EE subsystem |
| | |
EJB 3 subsystem | |
|
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) |
|
| La présence d'annotations JAX-RS dans le déploiement. |
Sous-système JCA |
|
| Le déploiement d'une archive d'adaptateur de ressources (RAR). |
Sous-système JPA (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 |
| |
Ces dépendances sont toujours ajoutées à moins que l'attribut
add-logging-api-dependencies soit défini à false.
|
Sous-système SAR | |
| Le déploiement d'une archive SAR. |
Sous-système de sécurité |
| | |
Sous-système Web | |
| Le déploiement d'une archive WAR. JavaServer Faces (JSF) est ajouté uniquement si utilisé. |
Sous-système de Services Web |
| | |
Sous-système Weld (CDI) | |
| La présence d'un fichier beans.xml dans le déploiement. |
3.9.2. Les modules inclus
3.9.3. Référence de descripteur de déploiement de structure de déploiement de JBoss
- 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 d'EAR.
- Ajouter des racines de ressources supplémentaires à une EAR.
Chapitre 4. Valves
4.1. Valves
- 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.
4.2. Valves globales
4.3. Les valves d'authentification
org.apache.catalina.authenticator.AuthenticatorBase
et elle remplace la méthode authenticate(Request request, Response response, LoginConfig config)
.
4.4. Configurer une application web pour utiliser une valve.
jboss-web.xml
.
Important
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 une archive 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.
Configuration d'une valve
Créer un élémentvalve
contenant l'élément enfantclass-name
au fichierjboss-web.xml
de l'application. Leclass-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>
Configurer une valve personnalisée
Si la valve possède des paramètres configurables, ajouter un élément enfantparam
à l'élémentvalve
pour chaque paramètre, en spécifiant leur nom et leur valeurparam-name
etparam-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>
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
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.
Procédure 4.2. Configurer une application pour utiliser une valve d'authentification
Configurer la valve
L'utilisation d'une valve locale nécessite une configuration dans le descripteur de déploiementjboss-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.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>
4.6. Créer une valve personnalisée
Procédure 4.3. Créer une valve personnalisée
Créer la classe Valve
Créer une sous-classe deorg.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 { }
Mettre en place la méthode d'invocation
La méthodeinvoke()
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) { }
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éthodegetNext().invoke()
.getNext().invoke(request, response);
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
5.1.2. Frameworks de journalisations d'applications pris en charge par JBoss LogManager
- JBoss Logging - inclus avec JBoss EAP 6
- Apache Commons Logging - http://commons.apache.org/logging/
- Simple Logging Facade Java (SLF4J) - http://www.slf4j.org/
- Apache log4j - http://logging.apache.org/log4j/1.2/
- Java SE Logging (java.util.logging) - http://download.oracle.com/javase/6/docs/api/java/util/logging/package-summary.html
5.1.3. Niveaux de journalisation
TRACE
, DEBOG
, INFO
, ATTENTION
, ERREUR
et FATAL
.
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 pas idéal. 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
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 nettoyages 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
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 d'enregistreurs d'événements pour le développement, en cours d'exécution.
5.2.3. Ajouter une journalisation à une application par JBoss Logging
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
- 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.
Ajouter Imports
Ajouter les déclarations d'importation des espace-noms de classe JBoss Logging que vous allez utiliser. Vous devrez importer au minimumimport org.jboss.logging.Logger
.import org.jboss.logging.Logger;
Créer un objet Logger
Créer une instance deorg.jboss.logging.Logger
et l'initialiser en utilisant la méthode statiqueLogger.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);
Ajouter les messages de journalisation
Ajouter des appels aux méthodes de l'objetLogger
à votre code, là où vous souhaitez qu'il envoie des messages. L'objetLogger
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ètremessage
comme string.LOGGER.error("Configuration file not found.");
Pour obtenir une liste complète des méthodes JBoss Logging, consulter le packageorg.jboss.logging
dans JBoss EAP 6 API Documentation.
Exemple 5.1. Utilisation de JBoss Logging quand on ouvre un fichier de propriétés
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
5.3.2. La journalisation par déploiement vers une application
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é.
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
ouJAR
.Déploiement
EAR
Copier le fichier de configuration de journalisation dans le répertoireMETA-INF
.Déploiement
WAR
ouJAR
Copier le fichier de configuration de journalisation dans le répertoireMETA-INF
ouWEB-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
- Un nom unique. Ceci est requis.
- N'importe quel nombre de log handlers.
- N'importe quelle catégorie log.
- Un root logger maximum.
MANIFEST.MF
, en utilisant l'attribut logging-profile
.
5.4.2. Spécifier un profil de journalisation dans une application
MANIFEST.MF
.
Conditions préalables :
- 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 fichierMANIFEST.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 fichierMANIFEST.MF
: ajouter la ligne suivante, en remplaçant NAME par le nom du profil qui convient.Logging-Profile: NAME
Note
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>
Chapitre 6. Internationalisation et localisation
6.1. Introduction
6.1.1. Internationalisation
6.1.2. Localisation
6.2. Outils JBoss Logging
6.2.1. Aperçu
6.2.1.1. JBoss Logging Tools - Internationalisation et Localisation
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.
6.2.1.2. 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.
6.2.1.3. Message Logger
@org.jboss.logging.MessageLogger
.
6.2.1.4. Lot de messages
@org.jboss.logging.MessageBundle
.
6.2.1.5. Messages de journalisation internationalisés
@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.
6.2.1.6. Exceptions internationalisées
6.2.1.7. Messages internationalisés
6.2.1.8. Fichiers de propriétés de traduction
6.2.1.9. Codes de projets de JBoss Logging Tools
projectCode
de l'annotation @MessageLogger
.
6.2.1.10. Ids de messages de JBoss Logging Tools
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
logging-tools
pour trouver un exemple complet.
Prérequis :
- Vous devez déjà posséder un projet Maven en cours. Voir Section 6.2.6.1, « Configuration Maven JBoss Logging Tools ».
- 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
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 qui impléemente cette interface. Procédez par la méthode
getMessageLogger()
deorg.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() ); }
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 estINFO
.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 deBasicLogger
(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.");
6.2.2.2. Créer et utiliser des messages internationalisés
logging-tools
pour trouver un exemple complet.
Pré-requis
- Vous avez un projet Maven utilisant le référentiel JBoss EAP 6. Voir Section 2.3.2, « Configurer le référentiel JBoss EAP 6 Platform Maven Repository par les paramètres de configuration de Maven ».
- 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
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); }
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();
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());
6.2.2.3. Créer des exceptions personnalisées
logging-tools
pour trouver un exemple complet.
Procédure 6.3. Créer et utiliser des exceptions internationalisées
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 »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); }
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);
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(); }
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
logging-tools
pour trouver un exemple complet.
Conditions préalables :
- Vous devez déjà posséder un projet Maven en cours.
- Le projet devra avoir été configuré pour JBoss Logging Tools.
- 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
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épertoiretarget/generated-translation-files
de votre projet Maven.Créer votre projet
Créer votre projet en utilisant Maven.[Localhost]$ mvn compile
@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.
InterfaceName
comme nom d'interface pour laquelle ce fichier a été créé : InterfaceName.i18n_locale_COUNTRY_VARIANT.properties
.
6.2.3.2. Traduire un logger, une exception ou un message internationalisé
logging-tools
pour trouver un exemple complet.
Pré-requis
- Vous devez déjà posséder un projet Maven en cours.
- Le projet devra avoir été configuré pour JBoss Logging Tools.
- Le projet devra contenir une ou plusieurs interfaces qui définissent les exceptions ou message log d'internationalisation.
- 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é
Créer des fichiers de propriétés modèles
Exécuter la commandemvn compile
pour créer les modèles de fichiers de propriétés de traduction.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épertoiresrc/main/resources
de votre projet. Les fichiers de propriétés doivent figurer dans le même package des interfaces qu'ils traduisent.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
.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é.
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
logging-tools
pour trouver un exemple complet.
Conditions préalables
- 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 ».
- 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
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 { }
Indiquer les ids de messages
Indiquer un id de message pour chaque message qui utilise un attributid
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();
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
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
Indiquer l'attribut du niveau
Ajouter l'attributlevel
à l'annotation de@LogMessage
de la définition de la méthode de message de journalisation.Allouer un niveau de journalisation
Allouer l'attributlevel
à la valeur du niveau de journalisation de ce message. Les valeurs valides delevel
sont les six constantes énumérées définies dansorg.jboss.logging.Logger.Level
:DEBUG
,ERROR
,FATAL
,INFO
,TRACE
, etWARN
.Import org.jboss.logging.Logger.Level; @LogMessage(level=Level.ERROR) @Message(value = "Customer query failed, Database not available.") void customerQueryFailDBClosed();
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
Procédure 6.8. Personnaliser les messages de journalisation avec les paramètres
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.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
%{#$}s
dans le message où # indique le numéro du paramètre que vous souhaitez apercevoir.
Important
@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, user:%2$s, customerid:%1$s") void customerLookupFailed(Long customerid, String username);
6.2.4.4. Indiquer une exception comme cause d'un message de journalisation
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.
@Cause
pour indiquer l'exception "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
Ajouter le paramètre
Ajouter un paramètreThrowable
ou une sous-classe à la méthode.@LogMessage @Message(id=404, value="Loading configuration failed. Config file:%s") void loadConfigFailed(Exception ex, File file);
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);
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 typeFileNotFoundException
.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
Conidtions préalables
- Vous devrez déjà avoir un projet avec des exceptions internationalisées. Voir Section 6.2.2.3, « Créer des exceptions personnalisées ».
- 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
Indiquer un code de projet
Indiquer le code de projet en utilisant l'attributprojectCode
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); }
Indiquer les ID de messages
Indiquer l'id de message pour chaque exception en utilisant l'attributid
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
Exemple 6.3. Créer des exceptions internationalisées
@MessageBundle(projectCode="ACCTS") interface ExceptionBundle { ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class); @Message(id=143, value = "The config file could not be opened.") IOException configFileAccessError(); }
throw ExceptionBundle.EXCEPTIONS.configFileAccessError();
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
Procédure 6.11. Personnaliser les messages d'exception avec les paramètres
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ésentationString
du paramètre est ce qui est affiché dans le message.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
%{#$}s
dans le message où#
indique le numéro du paramètre 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
@Cause
n'est pas inclus dans le nombre de paramètres.
Exemple 6.4. Utilisation des indexations ordinaires
@Message(id=2, value="Customer query failed, customerid:%s, user:%s") void customerLookupFailed(Long customerid, String username);
Exemple 6.5. Utilisation des indexations explicites
@Message(id=2, value="Customer query failed, user:%2$s, customerid:%1$s") void customerLookupFailed(Long customerid, String username);
6.2.5.3. Indiquer une exception comme cause d'une autre exception
@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.
@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
Ajouter le paramètre
Ajouter un paramètreThrowable
ou une sous-classe à la méthode.@Message(id=328, value = "Error calculating: %s.") ArithmeticException calculationError(Throwable cause, String msg);
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);
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
@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); }
int totalDue = 5; int daysToPay = 0; int amountPerDay; try { amountPerDay = totalDue/daysToPay; } catch (Exception ex) { throw CalcExceptionBundle.EXCEPTIONS.calcError(ex, "payments per day"); }
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
pom.xml
:
logging-tools
pour obtenir un exemple de fichier pom.xml
complet.
- Le référentiel JBoss Maven Repository doit être activé pour le projet. 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 dépendances Maven de
jboss-logging
etjboss-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 commeprovided
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>
- Le
maven-compiler-plugin
doit utiliser au minimum la version2.2
et être configuré pour la cible et les sources générées de1.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
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.
InterfaceName.i18n_locale_COUNTRY_VARIANT.properties
InterfaceName
est le nom de l'interface où les traductions s'appliquent.locale
,COUNTRY
, etVARIANT
identifient les paramètres régionaux correspondant à la traduction.locale
etCOUNTRY
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.
Exemple 6.7. Échantillon de fichier de propriétés de traductions
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
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
7.1.2. Groupe de fonctionnalités 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
- 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
- ne supportant que les fonctionnalités qui sont utiles pour les applications-web, et
- 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
- 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
- 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
Important
7.1.6. Écriture des beans Enterprise
7.1.7. Interfaces Métier de Session Bean
7.1.7.1. Interfaces de métier Bean Enterprise
7.1.7.2. Interfaces de métier locales EJB
7.1.7.3. Interfaces métier à distance EJB
7.1.7.4. Beans EJB 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
Conditions préalables
- Un serveur et un runtime de serveur pour JBoss EAP 6 a été installé. Veuillez vous référer à Section 1.3.1.5, « Ajouter le serveur de JBoss EAP en utilisant Define New Server » .
Procédure 7.1. Créer un projet EJB dans le Red Hat JBoss Developer Studio
Créer un nouveau projet
Pour ouvrir l'Assistant New EJB Project, naviguer jusqu'au menu Fichier, sélectionner Nouveau puis Projet EJB.Figure 7.1. Assistant New EJB Project
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 projetLe 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.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.Paramétrage du Module EJB
Cocher la case 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.Figure 7.2. Nouveau projet EJB créé dans Project Explorer
Ajouter l'artifact généré dans le serveur en vue du déploiement
Ouvrir le dialogue Ajouter et Supprimer 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 Disponible et cliquer sur le bouton Ajouter. La ressource sera déplacée dans la colonne Configurer, Cliquer sur Terminé pour fermer le dialogue.Figure 7.3. Ajouter et Supprimer le dialogue
Vous avez maintenant un Projet EJB dans Red Hat JBoss Developer Studio pouvant être généré et déployé pour un serveur spécifique.
7.2.2. Créer un projet EJB Archive dans Maven.
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
Créer le projet Maven
Un projet EJB peut être créé en utilisant le système archétype de Maven et l'archétypeejb-javaee6
. Pour ce faire, exécuter la commandemvn
avec les paramètres suivants :mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee6
Maven vous demandera legroupId
, leartifactId
, laversion
et lepackage
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]$
Ajouter vos beans enterprise
Ecrire vos beans enterprise et les ajouter au projet sous le répertoiresrc/main/java
dans le sous-répertoire approprié pour le paquetage du bean.Créer votre projet
Pour construire ce projet, exécuter la commandemvn package
dans le même répertoire que le fichierpom.xml
. Cela compilera les classes Java et empaquettera le fichier JAR. Le fichier JAR construit se nommeartifactId-version.jar
et se trouve dans le répertoiretarget/
.
7.2.3. Créer un projet EAR contenant un projet EJB
Conditions préalables
- Un serveur et un runtime de serveur pour JBoss EAP 6 a été installé. Veuillez vous référer à Section 1.3.1.5, « Ajouter le serveur de JBoss EAP en utilisant Define New Server ».
Procédure 7.3. Créer un projet EAR contenant un projet EJB
Ouvrir le nouvel Assistant de Projet de l'Application EAR.
Naviguer vers le menu Fichier, sélectionner Nouveau, puis Projet et l'assistant Nouveau Projet apparaîtra. Sélectionner Java EE/Enterprise Application Project et cliquer sur le bouton Suivant.Figure 7.4. Nouvel Assistant de Projet de l'Application EAR.
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 projetLe 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 auquel votre projet devra se conformer. Red Hat recommande d'en utiliser
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.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 :Ajouter un nouveau Module EJB.
Cliquer sur Nouveau Module, décocher la case Créer Modules par Défaut, sélectionner Enterprise Java Bean et cliquer sur Suivant. L'assistant Nouveau Projet EJB apparaîtra.Créer un Projet EJB.
L'assistant Nouveau Projet EJB 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 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.Optionnel : ajouter un descripteur de déploiement application.xml
Cocher la case Générer descripteur de déploiement d'application.xml le cas échéant.Cliquer sur Finish
Deux nouveaux projets vont apparaître, le projet EJB et le projet EAR.Ajouter l'artifact généré dans le serveur en vue du déploiement
Ouvrir la boîte de dialogue Ajouter et Supprimer en cliquant avec le bouton droit de la souris, dans l'onglet Serveurs, sur le serveur dans lequel vous souhaitez déployer l'artéfact de la build dans l'onglet serveur, et sélectionner Ajouter et Supprimer.Sélectionner la ressource EAR à déployer depuis la colonne Disponible et cliquer sur le bouton Ajouter. La ressource sera déplacée vers la colonne Configuré. Cliquer sur Terminé pour fermer la boîte de dialogue.Figure 7.5. Ajouter et Supprimer le dialogue
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
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.
Ouvrir le projet
Ouvrir un projet dans Red Hat JBoss Developer Studio.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).Figure 7.6. Ajouter un descripteur de déploiement
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
7.3.2. Stateless Session Beans
7.3.3. Stateful Session Beans
7.3.4. Singleton Session Beans
7.3.5. Ajouter des Session Beans à un Projet dans Red Hat JBoss Developer Studio
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
Ouvrir le projet
Ouvrir un projet dans Red Hat JBoss Developer Studio.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).Figure 7.7. Créer un assistant EJB 3.x Session Bean
Indiquer les informations de classe
Fournir les informations suivantes :- ProjetVerifier que le projet qui convient a bien été sélectionné.
- Dossier sourceIl s'agit du dossier dans lequel les fichiers source de Java seront créés. Cela n'a pas normalement besoin d'être changé.
- PackageIndiquer le package auquel cette classe appartient.
- Nom de la classeIndiquer le nom de la classe qui représentera le session bean.
- SuperclassLa classe de session bean peut hériter d'une Superclass. Indiquer ici si votre session a une Superclass.
- Type d'étatIndiquer le type d'état du session bean: stateless, stateful, ou singleton.
- Interfaces commercialesPar 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.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.
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.
Figure 7.8. Nouveau Session Bean dans Red Hat JBoss Developer Studio
7.4. Message-Driven Beans
7.4.1. Message-Driven Beans
7.4.2. Adaptateurs de ressources
7.4.3. Créer un Message-Driven Bean basé JMS dans Red Hat JBoss Developer Studio
Conditions préalables :
- Vous devrez avoir un projet existant ouvert dans Red Hat JBoss Developer Studio.
- Vous devrez connaître le nom et le type de la destination JMS que le bean écoutera.
- 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
Ouvrir l'assistant Create EJB 3.x Message-Driven Bean
Aller à File → New → Other. Sélectionner EJB/Message-Driven Bean (EJB 3.x) et cliquer sur le bouton Next.Figure 7.9. Créer l'assistant Message-Driven Bean EJB 3.x
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.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.
onMessage()
. Une fenêtre d'édition de Red Hat JBoss Developer Studio est apparue avec le fichier correspondant.
7.4.4. Spécifier un adaptateur de ressources dans jboss-ejb3.xml
pour un MDB
jboss-ejb3.xml
, vous pouvez spécifier un adaptateur de ressources pour utiliser un MDB. Sinon, pour configurer un adaptateur de ressources JBoss EAP 6 par défaut, voir Configurer les Message-Driven Beans dans le Guide d'Administration et de Configuration.
jboss-ejb3.xml
pour un MDB, utilisez l'exemple suivant :
Exemple 7.1. jboss-ejb3.xml
Exemple de configuration pour un adaptateur de ressource MDB
<jboss xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:jee="http://java.sun.com/xml/ns/javaee" xmlns:mdb="urn:resource-adapter-binding"> <jee:assembly-descriptor> <mdb:resource-adapter-binding> <jee:ejb-name>MyMDB</jee:ejb-name> <mdb:resource-adapter-name>MyResourceAdapter.rar</mdb:resource-adapter-name> </mdb:resource-adapter-binding> </jee:assembly-descriptor> </jboss>
<mdb:resource-adapter-name>
:
- Pour un adaptateur de ressource dans un autre EAR :
<mdb:resource-adapter-name>OtherDeployment.ear#MyResourceAdapter.rar</mdb:resource-adapter-name>
- Pour un adaptateur de ressource situé dans le même EAR que le MDB, vous pouvez omettre le nom de EAR :
<mdb:resource-adapter-name>#MyResourceAdapter.rar</mdb:resource-adapter-name>
7.4.5. Activer la substitution de propriété des EJB et MDB dans une application
@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
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é.
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èmeee
du fichier de configuration du serveur àtrue
.- 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 dustandalone/configuration/standalone-full.xml
. Si vous exécutez votre serveur dans un domaine géré, il s'agira du fichierdomain/configuration/domain.xml
. - Démarrer le serveur JBos EAP avec le profil complet.Dans Linux :
Dans Windows :EAP_HOME/bin/standalone.sh -c standalone-full.xml
EAP_HOMEbin\standalone.bat -c standalone-full.xml
- Lancer l'interface CLI par la commande pour votre système d'exploitation.Dans Linux :
Dans Windows :EAP_HOME/bin/jboss-cli.sh --connect
EAP_HOME\bin\jboss-cli.bat --connect
- Saisir la commande suivante pour activer la substitution de propriété d'annotation.
/subsystem=ee:write-attribute(name=annotation-property-replacement,value=true)
- Vous devriez voir apparaître le résultat suivant :
{"outcome" => "success"}
- 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>
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.
- Démarrez le serveur JBoss EAP et le Management API comme indiqué dans les étapes précédentes.
- 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 rapidehelloworld-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)
- 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.queue=java:/queue/HELLOWORLDMDBPropQueue -Dproperty.helloworldmdb.topic=java:/topic/HELLOWORLDMDBPropTopic -Dproperty.connection.factory=java:/ConnectionFactory
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 rapidehelloworld-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.- Modifier la valeur de propriété
@ActivationConfigProperty
destination
qui se trouve dans la classeHelloWorldQueueMDB
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") })
- Modifier la valeur de propriété
@ActivationConfigProperty
destination
qui se trouve dans la classeHelloWorldTopicMDB
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") })
- Modifier les annotations
@Resource
qui se trouvent dans la classeHelloWorldMDBServletClient
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;
- 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>
- 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
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.
Avertissement
Conditions préalables
- 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épendances 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.
Procédure 7.8. Ajouter une configuration de projet Maven pour l'invocation à distance des session beans
Ajouter les dépendances de projet utiles
Lepom.xml
du projet doit être mis à jour pour pouvoir inclure les dépendances nécessaires.Ajouter le fichier
jboss-ejb-client.properties
L'API du client JBoss EJB s'attend à trouver un fichier dans la racine du projet nomméjboss-ejb-client.properties
qui contient les informations de connexion aux services JNDI. Ajouter ce fichier au répertoiresrc/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 ligneSSL_ENABLED
àtrue
et dé-commenter la ligneSSL_STARTTLS
. L'interface éloignée du conteneur supporte les connexions sécurisées et non sécurisées en utilisant le même port.Ajouter des dépendances aux interfaces commerciales à distance.
Ajouter les dépendances Maven aupom.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>
Procédure 7.9. Obtenez un Bean Proxy par JNDI et invoquez les méthodes du Bean
Exceptions vérifiées par Handle
Deux des méthodes utilisées dans le code suivant (InitialContext()
etlookup()
) ont une exception vérifiée du typejavax.naming.NamingException
. Ces appels de méthode doivent soit être contenus dans un bloc try/catch qui intercepteNamingException
ou dans une méthode déclarée pour lancerNamingException
. Le Quickstartejb-remote
utilise la seconde technique.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 fichierjboss-ejb-client.properties
.Utiliser la méthode JNDI Context's lookup() pour obtenir un Bean Proxy
Invoquer le méthodelookup()
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 » .Méthodes d'invocation
Maintenant que vous avez un objet bean proxy, vous pouvez invoquer n'importe quelle méthode qui contienne 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.
7.5.2. Contextes Client EJB
- Un client à distance, exécuté comme application Java autonome.
- Un client à distance, exécuté dans une autre instance de JBoss EAP 6.
7.5.3. Considérations lors de l'utilisation d'un Contexte EJB Unique
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 » .
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(); ... } }
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 » .
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(); ... } }
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
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és 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.
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(); } }
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.
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é.
Note
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és 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
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
.
Procédure 7.10. Configurer un EJB en utilisant un contexte scoped basé mappage
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 fichierjboss-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 surtrue
. 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”);
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());
- 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
Les tableaux suivants énumèrent les propriétés pouvant être configurées par programmation ou dans le fichier jboss-ejb-client.properties
.
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.
Avertissement
Red Hat recommande que vous désactiviez explicitement SSL en faveur de TLSv1.1 ou TLSv1.2 dans tous les packages affectés.
|
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 d'étendue. La valeur par défaut est
false .
Si défini comme
true , le client EJB utilisera le contexte étendu 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.
|
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.
|
|
Nom d'hôte ou IP de cette connexion
|
|
Port de la connexion. La valeur par défaut est 4447.
|
|
Nom d'utilisateur utilisé pour authentifier la sécurité de connexion.
|
|
Mot de passe utilisé pour authentifier l'utilisateur.
|
|
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.
|
|
Nom complet de la classe
CallbackHandler . Il sera utilisé pour établir la connexion et ne peut être modifié tant que la connexion est activée.
|
|
Valeur entière indiquant le nombre maximal 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.
|
|
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é.
|
|
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 silencieux, utilisé lorsque le client et serveur sont sur la même machine, est désactivé.
|
|
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.
|
|
Valeur booléenne indiquant si le protocole SSL est activé pour cette connexion.
Avertissement
Red Hat recommande que vous désactiviez explicitement SSL en faveur de TLSv1.1 ou TLSv1.2 dans tous les packages affectés.
|
|
Intervalle pour envoyer une pulsation entre un client et un serveur pour empêcher une fermeture automatique, par exemple dans le cas d'un pare-feu. La valeur est exprimée en millisecondes.
|
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 |
---|---|
|
Le nom complet de l'implémentation de
org.jboss.ejb.client.ClusterNodeSelector .
Cette classe, plutôt que
org.jboss.ejb.client.DeploymentNodeSelector , 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 No ejb receiver available .
|
|
Valeur entière indiquant le nombre maximal de requêtes sortantes pouvant être faites au cluster entier.
|
|
Valeur entière indiquant le nombre maximal 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
ejb-jar.xml
pour la version 3.1 du descripteur de déploiement ejb-jar.
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.
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
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.
iAmAround
de l'invocation :
Exemple 7.2. 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(); } }
jboss-ejb3.xml
décrit ici : Section 7.6.3, « Configurer un intercepteur de conteneur ».
7.6.3. Configurer un intercepteur de conteneur
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
- Créer un fichier
jboss-ejb3.xml
dans le répertoireMETA-INF
du déploiement EJB. - Configurer les éléments de l'intercepteur du conteneur dans le fichier du descripteur.
- Utiliser l'espace-nom
urn:container-interceptors:1.0
pour indiquer la configuration des éléments de l'intercepteur du conteneur. - Utiliser l'élément
<container-interceptors>
pour indiquer les intercepteurs du conteneur. - 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.
- Vérifier les exemples d'éléments ci-dessus dans le fichier de descripteur suivant.
Exemple 7.3. 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-nomurn:container-interceptors:1.0
se trouve dansEAP_HOME/docs/schema/jboss-ejb-container-interceptors_1_0.xsd
.
7.6.4. Modifier l'identité du contexte de sécurité
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.
ejb-security-interceptors
pour obtenir un exemple complet.
Procédure 7.12. Modifier l'identité du contexte de sécurité
Créer l'intercepteur côté client
Cet intercepteur côté client doit implémenter l'interfaceorg.jboss.ejb.client.EJBClientInterceptor
. L'intercepteur doit passer l'identité requise par le mappage de données contextuelles, par l'intermédiaire d'un appelEJBClientInvocationContext.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 leEJBClientContext
par l'un des moyens suivants :Par programmation
Par cette approche, vous appelez la méthodeorg.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor)
et passez l'instanceorder
et l'instanceinterceptor
. L'instanceorder
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 fichierMETA-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 deServiceLoader
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 quickstartejb-security-interceptors
utilise cette approche.
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 ».Créer l'intercepteur de conteneur
Cet intercepteur reçoit leInvocationContext
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);; } } }
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 ».
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; }
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
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.
Procédure 7.13. Information de sécurité pour l'authentification EJB
Créer l'intercepteur côté client
Cet intercepteur doit implémenterorg.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 appelEJBClientInvocationContext.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 ».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 ».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);; } } }
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 ».
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 quickstartejb-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. }
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, leSaslPlusLoginModule
doit être mis dans la chaîne avant le LoginModule qui charge les rôles par l'optionpassword-stacking
.Configurer l'ordonnancement du LoginModule par le Management CLI
Ce qui suit est un exemple de commandes de Management CLI qui enchaînent leSaslPlusLoginModule
personnalisé avant que le LoginModuleRealmDirect
définisse l'optionpassword-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})
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 desecurity
du fichier de configuration du serveur. LeSaslPlusLoginModule
doit précéder le LoginModuleRealmDirect
pour qu'il puisse vérifier l'utilisateur distant avant que les rôles utilisateurs ne soient chargés et l'optionpassword-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>
Créer le client distant
Dans l'exemple de code suivant, on assume que le fichieradditional-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
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'APIorg.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor)
et passez l'instanceorder
et l'instanceinterceptor
. L'instanceorder
est utilisée pour déterminer où exactement cetinterceptor
est placé dans la chaîne d'intercepteur du client.Mécanisme ServiceLoader
Cette approche nécessite la création d'un fichierMETA-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 deServiceLoader
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 quickstartejb-security-interceptors
utilise cette approche.
7.7. JavaBeans Enterprise clusterisés
7.7.1. JavaBeans clusterisées (EJB)
Note
7.7.2. Configuration de client EJB In-server ou Autonome
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.
jboss-ejb-client.xml
) montre la configuration complète :
Exemple 7.4. 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
jboss-ejb-client.xml
et ajouter la configuration de cluster comme illustré dans l'exemple suivant :
Exemple 7.5. 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
7.7.3. Mettre en place une politique d'équilibrage des charges presonnalisée pour les appels EJB
AllClusterNodeSelector
pour les appels EJB calls. 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); } }
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
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);
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-la 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
-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
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
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.
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
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é.
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
UserTransaction
côté client, démarrer votre serveur avec la propriété système suivante -Djboss.node.name=yourServerName
et utilisez-la 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
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 fichierapplication.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 leejb-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, laissez 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. 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
@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 attributmappedName()
. La spécification le considère comme une métadonnée spécifique au fournisseur tandis que JBoss reconnaitmappedName()
en tant que nom JNDI global du EJB que vous référencez. Si vous avez indiqué unmappedName()
, 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 il 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 lebeanName()
est défini, il vous faudra utiliser le même algorithme que@EJB
sans attribut défini mis à partbeanName()
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
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 |
jboss-javaee-6.0
et jboss-as-ejb-client-bom
, ces dépendances doivent être ajoutées à la section <dependencies>
du fichier pom.xml
.
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
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>
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/
.
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é.
jboss-ejb3.xml
. Voir http://java.sun.com/xml/ns/javaee/ pour la documentation sur les espace-noms standard.
http://www.jboss.com/xml/ns/javaee
.
Espace-nom de descripteur d'assemblage
<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éfinirsecurity-domain
etrun-as-principal
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.6. 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
se crée
, démarre
, s'arrête
ou se détruit
lui-même.
- 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 classeServiceMBeanSupport
. La classeServiceMBeanSupport
fournit des implémentations des méthodes de cycle de vie de services commecreate
,start
etstop
. Pour un événement spécifique commestart()
, vous devrez surcharger la méthodestartService()
donnée par la classe ServiceMBeanSupport.
8.2. Exemple de MBean standard
.sar
).
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.
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 être mis en veille.
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; } }
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. Descripteur 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>
jboss-service.xml
dans le dossier META-INF
d'une archive de service (.sar
).
8.3. Déployer JBoss MBean Services
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
ServiceMBeanTest.sar
) en mode Standalone
, utliser la commande suivante :
[standalone@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar
[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
9.1.2. Cache de session web
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é.
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ètreowners
. Ce nombre de propriétaires est défini par défaut à2
.
Le paramètre owners
contrôle combien de nœuds de clusters détiennent des duplicata de la session. La valeur par défaut est 2
.
9.1.3. Configurer le cache de session web
REPL
. Si vous souhaitez utiliser le mode DIST
, exécutez les deux commandes suivantes dans l'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
Modifier le mode cache par défaut en
DIST
./profile=ha/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=dist)
Définir le nombre de propriétaires de cache distribué.
La commande suivante définit5
propriétaires. La valeur par défaut est2
./profile=ha/subsystem=infinispan/cache-container=web/distributed-cache=dist/:write-attribute(name=owners,value=5)
Modifier le mode cache par défaut en
REPL
./profile=ha/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=repl)
Relancer le serveur
Après avoir modifié le mode cache du web, vous devez relancer le serveur.
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
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
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 descriptifweb.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>
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 fichierjboss-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 de réplication
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
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.
Valeurs valides de
Note FIELD n'est pas pris en charge dans JBoss EAP 6.
|
Tableau 9.2. Options les moins communément modifiées pour les réplications de session
Option
|
Description
|
---|---|
<use-jk>
|
Présumons qu'un équilibreur de charge tel que
mod_cluster , mod_jk , ou mod_proxy est utilisé. La valeur par défaut est false . Si définie comme true , le conteneur examine l'ID de session associé à chaque requête et remplace la partie jvmRoute de l'ID de session s'il y a un basculement.
|
<max-unreplicated-interval>
|
Le plus grand intervalle (en secondes) à attendre après qu'on ait pu accéder à 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. 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
- 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.
- 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.
9.2.2. Configurer la passivation HttpSession dans votre application
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.
<passivation-config>
Eléments enfants
<use-session-passivation>
- 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ée avant que le temps<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 fichierweb.xml
. La valeur par défaut-1
désactive la passivation selon l'inactivité maximum.
Note
<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 owners
. Voir 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.
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.3. Implémenter un Singleton HA
La procédure suivante illustre comment déployer un service qui se trouve dans un decorateur 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
Rédiger une application de Service HA Singleton
Voici un exemple simple deService
se trouvant dans le decoratorSingletonService
devant être déployé comme service singleton. On trouvera un exemple complet dans le quickstart decluster-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.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); } } } }
Créer un activateur qui installe le
Service
en tant que singleton clusterisé.La liste suivante est un exemple d'activateur de service qui installe leHATimerService
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 disponibles pendant toute la durée du cycle de version EAP 6.x.Créer un fichier ServiceActivator
Créer un fichier nomméorg.jboss.msc.service.ServiceActivator
dans le répertoireresources/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
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.Créer une interface d'ordonnancement
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(); }
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(); } } }
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 profilHA
, 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.4. Démarrage de serveurs multiples autonomes dans 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.5. Démarrage de serveurs multiples 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 fichierstandalone-ha.xml
pour que chaque instance de serveur puisse se lier à une interface séparée.Déployer l'application dans les serveurs
La commande Maven suivante déploiera 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 quickstartcluster-ha-singleton
fourni dans JBoss EAP 6 pour la configuration et les détails de déploiement de Maven.
9.4. Apache mod_cluster-manager Application
9.4.1. L'application mod_cluster-manager
9.4.2. L'application mod_cluster-manager
Figure 9.1. La page web d'administration de mod_cluster
- [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.1. CDI
10.1.2. CDI (Contexts and Dependency Injection)
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épertoireMETA-INF/
ouWEB-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
10.1.5. Relation entre Weld, Seam 2, Seam 3, et JavaServer Faces
10.2. Utiliser CDI
10.2.1. Premières étapes
10.2.1.1. Activer CDI
Le CDI (Contexts and Dependency Injection) est l'une des technologies de base de JBoss EAP 6, et est activée par défaut. Si, pour une raison ou une autre, elle était désactivée et que vous souhaitiez l'activer, veuillez suivre la procédure suivante :
Procédure 10.1. Activer le CDI dans JBoss EAP 6
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 configurationdomain.xml
oustandalone.xml
, ou en supprimant la section qui convient.Pour trouver le sous-système dansEAP_HOME/domain/configuration/domain.xml
ouEAP_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"/>
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.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"/>
- 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"/>
Démarrer à nouveau JBoss EAP 6.
Démarrer JBoss EAP 6 avec votre configuration mise à jour.
JBoss EAP 6 démarre avec le sous-système CDI activé.
10.2.2. Utiliser le CDI pour développer une application
10.2.2.1. Utiliser le CDI pour développer une application
Le CDI (Contexts and Dependency Injection) vous offre une très grande flexibilité pour développer des applications, réutiliser des codes, adapter votre code au déploiement ou à l'exécution et tester des unités. JBoss EAP 6 comprend Weld, l'implémentation de référence de CDI. Ces tâches vous montrent comment utiliser CDI dans vos applications Enterprise.
10.2.2.2. CDI avec le code existant
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
Groupez vos beans dans une archive.
Groupez vos beans dans une archive JAR ou WAR.Inclure un fichier
beans.xml
dans votre archive.Mettez un fichierbeans.xml
dans votre répertoire d'archive JARMETA-INF/
ou d'archive WARWEB-INF/
. Ce fichier peut rester vide.
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
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.
- La première exclut toutes les classes Swing.
- La seconde exclut les classes Google Web Toolkit si le Google Web Toolkit n'est pas installé.
- La 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 commelow
. - La 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>
10.2.2.4. Utiliser une injection pour étendre une implémentation
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.
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
.
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.
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
- Il résout les annotations de qualificateur sur tous les beans qui implémentent le bean d'un point d'injection.
- Il filtre les beans désactivés. Les beans désactivés sont des beans @Alternative qui ne sont pas explicitement activés.
10.2.3.2. Qualificateurs
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 ambigue.
Cette tâche indique une injection ambigue et la retire par un qualificateur. Pour en savoir plus sur les injections ambigues, voir Section 10.2.3.1, « Dépendances ambigües et non-satisfaites ».
Exemple 10.5. Injections ambiguës
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 que l'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 ambigue avec un qualificateur
Créer une annotation de qualificateur nommée
@Translating
.@Qualifier @Retention(RUNTIME) @Target({TYPE,METHOD,FIELD,PARAMETERS}) public @interface Translating{}
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 + "!"); } ... }
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")); } }
Translating
Welcome
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
bean
.
@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
@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
.
10.2.4.3. Utiliser le CDI pour injecter un objet dans un bean
META-INF/beans.xml
ou WEB-INF/beans.xml
, chaque objet de votre déploiement peut être injecté par le CDI.
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 unTranslateController
public class TranslateController { @Inject TextTranslator textTranslator; ...
Utiliser les méthodes de l'objet injecté
Vous pouvez directement utiliser les méthodes de vos objets injectés. Partez du principe queTextTranslator
possède une méthodetranslate
.Exemple 10.7. Utiliser les méthodes de l'objet injecté
// in TranslateController class public void translate() { translation = textTranslator.translate(inputText); }
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 ... }
Utiliser l'interface
Instance(<T>)
pour obtenir les instances de façon programmatique.L'interfaceInstance
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); }
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
@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
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.
@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
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)); } }
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>
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
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.
@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; } } }
@Inject @Preferred PaymentStrategy paymentStrategy;
Exemple 10.11. Assigner un scope à une méthode Producer
@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
@Produces @Preferred @SessionScoped public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps, CheckPaymentStrategy cps ) { switch (paymentStrategy) { case CREDIT_CARD: return ccps; case CHEQUE: return cps; default: return null; } }
Note
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
@Named
. Nommer un bean vous permet de l'utiliser directement dans JSF (Java Server Faces).
@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
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 doncgreeterBean
.Utiliser le bean nommé dans une vue JSF
<h:form> <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/> </h:form>
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
Exemple 10.13. Définition d'alternative
@Alternative @Synchronous @Asynchronous public class MockPaymentProcessor implements PaymentProcessor { public void process(Payment payment) { ... } }
beans.xml
.
10.2.7.4. Remplacer une injection par une alternative
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
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é.
Définir l'alternative.
@Alternative @Translating public class MockTranslatingWelcome extends Welcome { public String buildPhrase(string city) { return "Bienvenue à " + city + "!"); } }
Substituer l'alternative.
Pour activer l'implémentation de substitution, ajouter le nom complet de la classe à votre fichierMETA-INF/beans.xml
ouWEB-INF/beans.xml
.<beans> <alternatives> <class>com.acme.MockTranslatingWelcome</class> </alternatives> </beans>
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
- scope par défaut
- un groupe de liaisons d'intercepteur
- 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
@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
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
Définir le stéréotype
@Secure @Transactional @RequestScoped @Named @Stereotype @Retention(RUNTIME) @Target(TYPE) public @interface BusinessComponent { ... }
Utilisation d'un stéréotype.
@BusinessComponent public class AccountManager { public boolean transfer(Account a, Account b) { ... } }
Les stéréotypes rationalisent et simplifient votre code.
10.2.9. Méthodes Observer
10.2.9.1. Méthodes Observer
10.2.9.2. Observateurs transactionnels
public void refreshCategoryTree(@Observes(during = AFTER_SUCCESS) CategoryUpdateEvent event) { ... }
- IN_PROGRESS: les obervateurs sont alors invoqués immédiatement.
- AFTER_SUCCESS: les observateurs sont invoqués après la phase de transaction, mais seulement si la transaction aboutit.
- AFTER_FAILURE: les observateurs sont invoqués après la phase de transaction, mais seulement si la transaction échoue avant d'aboutir.
- AFTER_COMPLETION: les observateurs sont invoqués après la phase de complétion de la transaction.
- BEFORE_COMPLETION: les observateurs sont invoqués avant la phase de complétion de la transaction.
import javax.ejb.Singleton; import javax.enterprise.inject.Produces; @ApplicationScoped @Singleton public class Catalog { @PersistenceContext EntityManager em; List<Product> products; @Produces @Catalog List<Product> getCatalog() { if (products==null) { products = em.createQuery("select p from Product p where p.deleted = false") .getResultList(); } return products; } }
import javax.enterprise.event.Event; @Stateless public class ProductManager { @PersistenceContext EntityManager em; @Inject @Any Event<Product> productEvent; public void delete(Product product) { em.delete(product); productEvent.select(new AnnotationLiteral<Deleted>(){}).fire(product); } public void persist(Product product) { em.persist(product); productEvent.select(new AnnotationLiteral<Created>(){}).fire(product); } ... }
import javax.ejb.Singleton; @ApplicationScoped @Singleton public class Catalog { ... void addProduct(@Observes(during = AFTER_SUCCESS) @Created Product product) { products.add(product); } void removeProduct(@Observes(during = AFTER_SUCCESS) @Deleted Product product) { products.remove(product); } }
10.2.9.3. Appliquer et observer des événements
Exemple 10.15. Appliquer un événement
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
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
@Observes
.
public class AccountObserver { void checkTran(@Observes Withdrawal w) { ... } }
Exemple 10.18. Oberver un événement qualifié
public class AccountObserver { void checkTran(@Observes @Suspicious Withdrawal w) { ... } }
10.2.10. Intercepteurs
10.2.10.1. Les intercepteurs
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
- 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
Définir le type de liaison d'intercepteur.
@InterceptorBinding @Retention(RUNTIME) @Target({TYPE, METHOD}) public @interface Secure {}
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(); } }
Utiliser l'intercepteur dans votre code commercial.
@Secure public class AccountManager { public boolean transfer(Account a, Account b) { ... } }
Activer l'intercepteur dans votre déploiement, en l'ajoutant à votre fichier
META-INF/beans.xml
ouWEB-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é.
Le CDI simplifie votre code d'intercepteur et simplifie l'application de votre code commercial.
10.2.11. Les décorateurs
@Decorator
. Pour invoquer un décorateur dans une application CDI, il doit être dans le fichier beans.xml
.
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); ... } }
@Delegate
pour obtenir une référence à l'objet décoré.
10.2.12. Extensions portables
- 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
- 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
Types Java ne pouvant être mis en proxy par le conteneur
- Classes ne possédant pas de constructeur non-privé sans paramètre
- Classes déclarées
final
ou qui possèdent une méthodefinal
- Matrices et types primitifs
10.2.13.2. Utiliser un Proxy ou une Injection
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.
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); } }
Chapitre 11. Java Transaction API (JTA)
11.1. Aperçu
11.1.1. Java Transactions API (JTA)
Ces topics vous donnent des explications de base sur l'API Java Transactions (JTA).
11.2. Concepts de transactions
11.2.1. Transactions
11.2.2. Les propriétés ACID de transactions
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
11.2.4. Participants à une transaction
11.2.5. JTA (Java Transactions API)
11.2.6. JTS (Java Transaction Service)
Note
11.2.7. Sources de données XA et transactions XA
11.2.8. Recouvrement XA
11.2.9. Le protocole de validation en 2-Phases
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.
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
11.2.11. Les transactions distribuées
Note
11.2.12. API de portabilité ORB
Classes API de la portabilité ORB
com.arjuna.orbportability.orb
com.arjuna.orbportability.oa
11.2.13. Transactions imbriquées
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.
11.3. Optimisations de transactions
11.3.1. Optimisations de transactions
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)
11.3.2.1. Commit Markable Resource (CRM)
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.
- Préparer 2PC
- Valider LRCO
- Write tx log
- Commit 2PC
Une transaction peut ne contenir qu'une ressource CMR.
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
CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))
CREATE TABLE xids (xid RAW(144), transactionManagerID varchar(64), actionuid RAW(28)) CREATE UNIQUE INDEX index_xid ON xids (xid)
CREATE TABLE xids (xid VARCHAR(255) for bit data not null, transactionManagerID varchar(64), actionuid VARCHAR(255) for bit data not null) CREATE UNIQUE INDEX index_xid ON xids (xid)
CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28)) CREATE UNIQUE INDEX index_xid ON xids (xid)
CREATE TABLE xids (xid bytea, transactionManagerID varchar(64), actionuid bytea) CREATE UNIQUE INDEX index_xid ON xids (xid)
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
/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")
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)
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
11.3.3. Optimisation Presumed-Abort
11.3.4. Optimisation Lecture-seule
11.4. Résultats de transactions
11.4.1. Résultats de transactions
- Roll-back
- Si un participant à une transaction ne peut pas effectuer une validation, ou si le coordinateur de transactions ne peut pas indiquer aux participants de valider, la transaction sera annulée. Voir Section 11.4.3, « Transactions Roll-Back » pour plus d'informations.
- Valider
- Si chaque participant à une transaction peut valider, le coordinateur de transactions leur indique comment procéder, Voir Section 11.4.2, « Transactions de validation » pour plus d'informations.
- Résultat heuristique
- Si certains participants à une transaction valident, et que d'autres annulent, on parle de résultat heuristique. Les résultats heuristiques dérivent d'interventions humaines. Voir Section 11.4.4, « Résultats heuristiques » pour plus d'informations.
11.4.2. Transactions de validation
11.4.3. Transactions Roll-Back
11.4.4. Résultats heuristiques
- Rollback heuristique
- L'opération de validation a échoué car certains ou tous les participants ont abandonné la transaction.
- Validation heuristique
- Une opération de tentative de restauration a échoué, car tous les participants ont validé unilatéralement. Cela peut se produire si, par exemple, le coordinateur est en mesure de préparer correctement la transaction, mais décide alors de l'annuler en raison d'une défaillance de son côté, comme un oubli de mettre à jour son journal. Dans l'intervalle, les participants peuvent décider de valider.
- Mixed heuristiques
- Certains participants ont validé et d'autres ont abandonné
- Danger heuristique
- Le résultat de certaines mises à jour est inconnu. Pour les connues, ils ont soit tous été validés ou été annulés.
11.4.5. Erreurs et exceptions pour les transactions JBoss
UserTransaction
, voir la spécification UserTransaction API dans http://docs.oracle.com/javaee/6/api/javax/transaction/UserTransaction.html.
11.5. Aperçu sur les transactions JTA
11.5.1. JTA (Java Transactions API)
11.5.2. Cycle de vie d'une transaction JTA
Votre application démarre une nouvelle transaction
Pour démarrer une transaction, votre application doit obtenir une instance de la classeUserTransaction
du JNDI ou bien, s'il s'agit d'un EJB, en provenance d'une annotation. L'interfaceUserTransaction
comprend des méthodes pour commencer, valider, ou annuler des transactions au plus haut niveau. Les transactions nouvellement créées sont associées automatiquement à leur thread invoquant. Les transactions imbriquées ne sont pas supportées dans JTA, donc toutes les transactions sont des transactions au plus haut niveau.En appelantUserTransaction
.begin()
avec des annotations démarre une transaction quand une méthode EJB est appelée (bassée sur des règles d'attributs de transaction). Toute ressource qui est utilisée après cela sera associée à la transaction. S'il y a plus plus d'une seule ressource listée, votre transaction devient une transaction XA, et participera au protocole de validation en deux temps.Note
L'objetUserTransaction
est utilisé uniquement pour les transactions BMT. Dans CMT, l'objet UserTransaction n'est pas autorisé.Votre application modifie son état.
Dans l'étape suivante, votre application effectue son travail et précède aux changements d'état.Votre application décide de valider ou de s'annuler
Quand votre application a fini de changer d'état, elle décide si elle doit valider ou s'annuler. Elle appelle la méthode qui convient. Elle appelleUserTransaction
.commit()
ouUserTransaction
.rollback()
.Le gestionnaire de transactions supprime la transaction des archives.
Après la validation ou l'annulation, le gestionnaire de transactions nettoie ses archives et supprime les informations du journal de transaction sur votre transaction.
Le recouvrement d'échec a lieu automatiquement. Si une ressource, un participant à une transaction, ou un serveur d'applications est disponible, le gestionnaire de transactions s'occupe du recouvrement quand l'échec sous-jacent est résolu.
11.6. Configuration de sous-système de transaction
11.6.1. Configuration des transactions
Les procédures suivantes vous montrent comment configurer le sous-système de transactions de JBoss EAP 6.
11.6.2. Configuration de source de données transactionnelle
11.6.2.1. Configurez votre source de données pour utiliser l'API de transaction JTA
Cette tâche vous montre comment activer un JTA (Java Transaction API) dans votre source de données.
Vous devez remplir les conditions suivantes avant de continuer cette tâche :
- Votre base de données ou autre ressource devra supporter un API JTA. Dans le doute, veuillez consulter la documentation.
- Créer une source de données. Veuillez vous référer à Section 11.6.2.4, « Créer une source de données non-XA avec les interfaces de gestion ».
- Stopper le serveur JBoss EAP 6
- Obtenir un accès pour pouvoir éditer les fichiers de configuration directement dans un éditeur de texte.
Procédure 11.1. Configurez la source de données pour utiliser l'API de Java Transaction
Ouvrir le fichier de configuration dans l'éditeur de texte.
Selon que vous exécutiez JBoss EAP 6 sur un domaine géré ou un serveur autonome, votre fichier de configuration ne se trouvera pas au même endroit.Domaine géré
Le fichier de configuration par défaut d'un domaine géré se trouve dansEAP_HOME/domain/configuration/domain.xml
pour Red Hat Enterprise Linux, etEAP_HOME\domain\configuration\domain.xml
pour Microsoft Windows Server.Serveur autonome
Le fichier de configuration par défaut d'un serveur autonome se trouve dansEAP_HOME/standalone/configuration/domain.xml
pour Red Hat Enterprise Linux, etEAP_HOME\standalone\configuration\domain.xml
pour Microsoft Windows Server.
Chercher la balise
<datasource>
qui correspond à votre source de données.La source de données aura un attributjndi-name
correspondant à celui que vous aviez indiqué quand vous l'avez créée. Par exemple, la source de données ExampleDS ressemble à ceci :<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="H2DS" enabled="true" jta="true" use-java-context="true" use-ccm="true">
Définir l'attribut
jta
àtrue
.Ajouter l'élément suivant au contenu de votre balise<datasource>
, tel qu'il apparaît à l'étape précédente :jta="true"
Sauf si vous avez un cas d'utilisation spécifique (comme par exemple, définir une source de données en lecture seule), Red Hat décourage la substitution de la valeur par défautjta=true
. Ce paramètre indique que la source de données honorera l'API Java Transaction et permettra un meilleur suivi des connexions par l'implémentation JCA.Sauvegarder le fichier de configuration.
Sauvegarder le fichier de configuration et sortir de l'éditeur de texte.Démarrer JBoss EAP 6.
Relancer le serveur JBoss EAP 6.
JBoss EAP 6 démarre, et votre source de données est configurée pour utiliser l'API de transactions JTA.
11.6.2.2. Configuration d'une source de données XA
Pour pouvoir ajouter une source de données XA, vous devrez vous connecter à la console de gestion. Voir Section 11.6.2.3, « Se conncecter à la console de gestion » pour plus d'informations.
Ajouter une nouvelle source de données.
Ajouter une nouvelle source de données à la plateforme JBoss EAP 6. Suivre les instructions qui se trouvent dans Section 11.6.2.4, « Créer une source de données non-XA avec les interfaces de gestion », puis, cliquer sur l'onglet XA Datasource en haut.Configurer les propriétés supplémentaires suivant les besoins.
Tous les paramètres de la source de données se trouvent dans Section 11.6.2.6, « Paramètres de source de données ».
Votre source de données XA est configurée et elle est prête à l'utilisation.
11.6.2.3. Se conncecter à la console de gestion
Conditions préalables
- Vous devez créer un utilisateur administratif. Pour obtenir des instructions complètes, voir Add the User for the Management Interfaces qui se trouve dans le guide Administration and Configuration Guide de JBoss Enterprise Application Platform.JBoss EAP doit être en cours d'exécution.
Naviguer vers la page de démarrage de la console de gestion
Lancez votre navigateur web et accédez à la Console de gestion dans votre navigateur web dans http://localhost:9990/console/App.htmlNote
Port 9990 est prédéfini en tant que liaison de socket de Console de gestion.- Saisir le nom d'utilisateur et le mot de passe du compte que vous avez déjà créés pour vous connecter à l'écran de connexion de la console de gestion.
Figure 11.1. Écran de connexion de la console de gestion
Une fois connecté, vous serez redirigé à l'adresse suivante et la page d'accueil de la Console de gestion apparaîtra : http://localhost:9990/console/App.html#home
11.6.2.4. Créer une source de données non-XA avec les interfaces de gestion
Cette section explique les étapes à suivre pour créer une source de données non-XA, en utilisant la console de gestion ou l'interface CLI.
Conditions préalables
- Le serveur JBoss EAP 6 doit être en cours d'exécution.
Note
Procédure 11.2. Créer une source de données en utilisant l'interface CLI ou la console de gestion
Interface CLI
- Lancer le CLI et connectez-vous à votre serveur.
- Exécuter la commande d'interface de gestion CLI suivante pour créer une source de données non-XA, et configurer les variables comme il se doit :
Note
La valeur de DRIVER_NAME (nom de pilote) dépend du nombre de classes répertoriées dans le fichier/META-INF/services/java.sql.Driver
situé dans le JAR du pilote JDBC. S'il n'y a qu'une seule classe, la valeur correspondra au nom du JAR. S'il y a plusieurs classes, la valeur correspondra au nom du JAR + driverClassName + « _ » + majorVersion + « _ » + minorVersion. Toute erreur provoquera l'erreur suivante dans le journal :JBAS014775: New missing/unsatisfied dependencies
Par exemple, la valeur de DRIVER_NAME qu'il nous faut pour le pilote MySQL 5.1.31 estmysql-connector-java-5.1.31-bin.jarcom.mysql.jdbc.Driver_5_1
.data-source add --name=DATASOURCE_NAME --jndi-name=JNDI_NAME --driver-name=DRIVER_NAME --connection-url=CONNECTION_URL
- Activer la source de données :
data-source enable --name=DATASOURCE_NAME
Console de gestion
- Connectez-vous à la console de gestion.
Naviguer dans le panneau Datasources qui se trouve dans la console de gestion
- Sélectionner Configuration qui se trouve en haut de la console.
- En mode de domaine uniquement, sélectionner un profil à partir du menu déroulant qui se trouve en haut et à gauche.
- Étendre le menu Subsystems qui se trouve à gauche de la console, puis étendre le menu Connector.
- Sélectionner Datasources à partir du menu à gauche de la console.
Créer une nouvelle source de données
- Cliquer sur Add qui se trouve en haut du panneau Datasources.
- Saisir les attributs de la nouvelle source de données de l'assistant Create Datasource et appuyez sur Next.
- Saisir les informations sur le pilote JDBC dans l'assistant Create Datasource et cliquer sur Next pour continuer.
- Saisir les paramètres de connexion dans l'assistant Create Datasource.
- Cliquer sur le bouton Test Connection pour tester la connexion à la ressource de données et vérifier que les paramètres de configuration sont corrects.
- Cliquer sur Done pour terminer.
La source de données non-Xa a été ajoutée au serveur. Elle est maintenant visible dans le fichier standalone.xml
ou le fichier domain.xml
, ainsi que dans les interfaces de gestion.
11.6.2.5. Configuration des paramètres de validation de connexion de la base de données
En raison de problèmes de maintenance de base de données, de problèmes de réseau ou autres événements peuvent amener JBoss EAP 6 à perdre la connexion à la base de données. Vous activez la validation de la connexion à la base de données par l'élément < validation >
dans la section < datasource >
du fichier de configuration du serveur. Suivez les étapes ci-dessous pour configurer les paramètres de source de données pour activer la validation de connexion de la base de données dans JBoss EAP 6.
Procédure 11.3. Configuration des paramètres de validation de connexion de la base de données
Choisissez une méthode de validation
Veuillez sélectionner une des méthodes de validation suivantes.<validate-on-match>true</validate-on-match>
Lorsque l'option< validate-on-match >
a comme valeurtrue
, la connexion à la base de données est validée à chaque retrait du pool de connexions en utilisant le mécanisme de validation spécifié à l'étape suivante.Si une connexion n'est pas valide, un avertissement sera inscrit dans le journal et la prochaine connexion sera extraite du pool. Ce processus se poursuit jusqu'à ce qu'une connexion valide soit enfin trouvée. Si vous préférez ne pas faire défiler chaque connexion du pool, vous pouvez utiliser l'option<use-fast-fail>
. Si on ne trouve pas de connexion valide dans le pool, une nouvelle connexion sera créée. Si la création de la connexion échoue, une exception sera retournée à l'application qui en a fait la demande.Ce paramètre entraîne une récupération plus rapide, mais crée une charge plus élevée sur la base de données. Cependant, c'est la sélection la plus sûre si une baisse de performance minimale n'est pas un sujet de préoccupation.<background-validation>true</background-validation>
Lorsque l'option< >background-validation
a comme valeurtrue
, il est utilisé en combinaison à la valeur<background-validation-millis>
pour déterminer la fréquence d'exécution de l'information en d'arrière-plan. La valeur par défaut du paramètre<background-validation-millis>
est de 0 millisecondes, ce qui signifie que c'est désactivé par défaut. Cette valeur ne doit pas être à la même valeur que celle du paramètre< idle-timeout-minutes >
.Il est délicat de déterminer la valeur optimale de<background-validation-millis>
pour un système particulier. Plus la valeur est faible, le plus fréquemment le pool sera validé et le plus rapidement les connexions plus tôt non valides sont supprimées du pool. Cependant, des valeurs les plus faibles prennent davantage de ressources de base de données. En outre, les valeurs élevées ont des contrôles de validation de connexion plus fréquents et utilisent moins de ressources de base de données, mais les liens morts demeurent indétectables pendant de plus longues périodes.
Note
Si l'option<validate-on-match>
est définie àtrue
, l'option<background-validation>
devra être définie àfalse
. Le contraire est vrai également. Si l'option<background-validation>
est définie àtrue
, l'option<validate-on-match>
devra être définie àfalse
.Choisir un mécanisme de validation
Veuillez sélectionner un des mécanismes de validation suivants.Spécifier un Nom de classe <valid-connection-checker>
Il s'agit du mécanisme préféré car il est optimisé pour un RBMS particulier. JBoss EAP 6 fournit les vérificateurs de connexions suivants :- org.jboss.jca.adapters.jdbc.extensions.db2.DB2ValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLReplicationValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.novendor.JDBC4ValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.novendor.NullValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker
- org.jboss.jca.adapters.jdbc.extensions.sybase.SybaseValidConnectionChecker
Indiqué l'énoncé SQL pour <check-valid-connection-sql>
Vous fournissez l'énoncé SQL utilisé pour valider la connexion.Ce qui suit est un exemple qui vous montre comment spécifier un énoncé SQL pour valider une connexion dans Oracle :<check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>
Dans MySQL ou PostgreSQL, vous pouvez spécifier l'énoncé SQL suivant :<check-valid-connection-sql>select 1</check-valid-connection-sql>
Définir le Nom de classe <exception-sorter>
Lorsqu'une exception est marquée comme étant fatale, la connexion est fermée immédiatement, même si la connexion participe à une transaction. Utilisez l'option de classe de triage d'exception trieuse pour détecter correctement et ensuite nettoyer les exceptions de connexion fatales. JBoss EAP 6 fournit les trieurs d'exception suivants :- org.jboss.jca.adapters.jdbc.extensions.db2.DB2ExceptionSorter
- org.jboss.jca.adapters.jdbc.extensions.informix.InformixExceptionSorter
- org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter
- org.jboss.jca.adapters.jdbc.extensions.novendor.NullExceptionSorter
- org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter
- org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter
- org.jboss.jca.adapters.jdbc.extensions.sybase.SybaseExceptionSorter
11.6.2.6. Paramètres de source de données
Tableau 11.1. Les paramètres de source de données communs aux sources XA ou non-XA
Paramètre | Description |
---|---|
jndi-name | Le nom JNDI unique pour la source de données. |
pool-name | Le nom du pool de gestion de la source de données. |
enabled | Indique si la source de données est activée. |
use-java-context |
Indique si on doit relier la source de données au JNDI global.
|
spy |
Activer la fonctionnalité
spy sur la couche JDBC. Cela journalisera tout le trafic JDBC dans la source de données. Notez que la catégorie de journalisation jboss.jdbc.spy doit également être définie au niveau DEBUG dans le sous-système de journalisation.
|
use-ccm | Activer le gestionnaire de connexion cache. |
new-connection-sql | Un énoncé SQL qui exécute quand la connexion est ajoutée au pool de connexion. |
transaction-isolation |
Un parmi :
|
url-selector-strategy-class-name | Une classe qui implémente l'interface org.jboss.jca.adapters.jdbc.URLSelectorStrategy . |
sécurité |
Contient des éléments dépendants en tant que paramètres de sécurité. Voir Tableau 11.6, « Paramètres de sécurité ».
|
validation |
Contient des éléments dépendants en tant que paramètres de validation. Voir Tableau 11.7, « Paramètres de validation ».
|
timeout |
Contient des éléments dépendants en tant que paramètres de timeout. Voir Tableau 11.8, « Paramètres de timeout ».
|
énoncé |
Contient des éléments dépendants en tant que paramètres d'énoncés. Voir Tableau 11.9, « Paramètres d'instruction ».
|
Tableau 11.2. Paramètres de source de données non-xa
Paramètre | Description |
---|---|
jta | Active l'intégration JTA pour les sources de données non-XA. Ne s'applique pas aux sources de données XA. |
connection-url | L'URL de connexion du pilote JDBC. |
driver-class | Le nom complet de la classe de pilote JDBC. |
connection-property |
Propriétés de connexions arbitraires passées à la méthode
Driver.connect(url,props) . Chaque connection-property indique une paire name/value. Le nom de la propriété provient du nom, et la valeur provient du contenu de l'élément.
|
pool |
Contient des éléments dépendants en tant que paramètres de pooling. Voir Tableau 11.4, « Les paramètres de pool communs aux sources XA ou non-XA ».
|
url-delimiter |
Le délimiteur d'URLs d'une connexion url pour les bases de données clusterisées HA (Haute disponibilité).
|
Tableau 11.3. Paramètres de source de données XA
Paramètre | Description |
---|---|
xa-datasource-property |
Une propriété pour assigner la classe d'implémentation
XADataSource . Spécifiée par name=value. Si une méthode setter existe, dans le format setName , la propriété sera définie en appelant une méthode setter sous le format setName(value) .
|
xa-datasource-class |
Le nom complet de la classe d'implémentation de
javax.sql.XADataSource .
|
pilote |
Unique référence au module de chargeur de classe qui contient le pilote JDBC. Le format accepté est driverName#majorVersion.minorVersion.
|
xa-pool |
Contient des éléments dépendants en tant que paramètres de pooling. Voir Tableau 11.4, « Les paramètres de pool communs aux sources XA ou non-XA » et Tableau 11.5, « Paramètres du pool XA ».
|
recouvrement |
Contient des éléments dépendants en tant que paramètres de recouvrement. Voir Tableau 11.10, « Paramètres de recouvrement ».
|
Tableau 11.4. Les paramètres de pool communs aux sources XA ou non-XA
Paramètre | Description |
---|---|
min-pool-size | Le nombre minimum de connexions contenues par un pool. |
max-pool-size | Le nombre maximum de connexions qu'un pool peut contenir |
Pré-remplissage | Indique si l'on doit essayer de pré-remplir un pool de connexion. Un élément vide indique une valeur true . La valeur par défaut est false . |
use-strict-min | Indique si la taille du pool est stricte. false par défaut. |
flush-strategy |
Indique si le pool est vidé en cas d'erreur. Les valeurs acceptées sont :
La valeur par défaut est
FailingConnectionOnly .
|
allow-multiple-users | Indique si plusieurs utilisateurs pourront avoir accès à la source de données par la méthode getConnection(user, password), et si les types de pools internes ont une influence sur ce comportement. |
Tableau 11.5. Paramètres du pool XA
Paramètre | Description |
---|---|
is-same-rm-override | Indique si la classe javax.transaction.xa.XAResource.isSameRM(XAResource) retourne true ou false . |
entrelacement | Indique si on doit activer l'entrelacement pour les fabriques de connexion XA. |
no-tx-separate-pools |
Indique si on doit créer des sous-répertoires distincts pour chaque contexte. Cela est nécessaire pour les sources de données Oracle, qui ne permettent pas aux connexions XA d'être utilisées à la fois à l'intérieur et à l'extérieur d'une transaction de JTA
Utiliser cette option entraînera la multiplication par deux de la taille du pool
max-pool-size , car en fait, deux pools seront créés.
|
pad-xid | Indique si on doit remplir le Xid. |
wrap-xa-resource |
Indique si on doit inclure XAResource dans une instance
org.jboss.tm.XAResourceWrapper .
|
Tableau 11.6. Paramètres de sécurité
Paramètre | Description |
---|---|
user-name | Le nom d'utilisation pour créer une nouvelle connexion. |
password | Le mot de passe à utiliser pour créer une nouvelle connexion |
security-domain | Contient le nom d'un gestionnaire de sécurité JAAS, qui gère l'authentification. Ce nom correspond à l'attribut application-policy/name de la configuration de connexion JAAS. |
reauth-plugin | Définit un plugin d'authentification à nouveau pour la réauthentification de connexions physiques. |
Tableau 11.7. Paramètres de validation
Paramètre | Description |
---|---|
valid-connection-checker |
Une mise en œuvre d'interface
org.jboss.jca.adaptors.jdbc.ValidConnectionChecker qui fournit une méthode SQLException.isValidConnection(Connection e) pour valider une connexion. Une exception signifie que la connexion est détruite. Cela remplace le paramètre check-valid-connection-sql s'il est présent.
|
check-valid-connection-sql | Un énoncé SQL pour vérifier la validité d'un pool de connexion. Peut être appelé quand une connexion gérée est tirée d'un pool. |
validate-on-match |
Indique si la validation de niveau de connexion est exécutée lorsqu'une fabrique de connexions essaie de correspondre à une connexion gérée pour un ensemble donné.
Indiquer "true" pour
validate-on-match n'est pas normalement fait en conjonction avec "true" pour background-validation . Validate-on-match est utile quand un client doit avoir une connexion vallidée avant utilisation. Ce paramètre est à false par défaut.
|
background-validation |
Indique que les connexions sont validées sur un thread d'arrière-plan. La validation de l'arrière-plan (Background validation) est une optimisation de performance lorsque non utilisé avec
validate-on-match . Si Validate-on-match est sur true, l'utilisation de background-validation pourrait entraîner des contrôles redondants. La validation de l'arrière-plan pourrait provoquer une mauvaise connexion (une connexion qui irait mal entre le moment de l'analyse de validation et avant d'être donnée au client), l'application cliente doit par conséquent tenir compte de cette possibilité.
|
background-validation-millis | La durée, en millisecondes, pendant laquelle la validation d'arrière-plan exécute. |
use-fast-fail |
Si défini sur true, échoue une allocation de connexion lors de la première tentative, si la connexion est non valide. La valeur par défaut est
false .
|
stale-connection-checker |
Une instance de
org.jboss.jca.adapters.jdbc.StaleConnectionChecker qui produit une méthode booléenne isStaleConnection(SQLException e) . Si cette méthode renvoie un true , l'exception sera contenue dans org.jboss.jca.adapters.jdbc.StaleConnectionException , qui correspond à une sous-classe de SQLException .
|
exception-sorter |
Une instance de
org.jboss.jca.adapters.jdbc.ExceptionSorter qui fournit une méthode booléenne isExceptionFatal(SQLException e) . Cette méthode validera si une exception est envoyée à toutes les instances d'un javax.resource.spi.ConnectionEventListener en tant que message connectionErrorOccurred .
|
Tableau 11.8. Paramètres de timeout
Paramètre | Description |
---|---|
use-try-lock | Utiliser tryLock() au lieu de lock() . Vous essayerez ainsi d'obtenir un verrou pour le nombre de secondes configurées, avant le timeout, au lieu d'échouer immédiatement quand le verrou n'est pas disponible. La valeur par défaut est de 60 secondes. Par exemple, pour définir un timeout de 5 minutes, définir <use-try-lock> 300</use-try-lock> . |
blocking-timeout-millis | La durée maximale, en millisecondes, de blocage lorsque vous attendez une connexion. Après que ce délai soit dépassé, une exception sera levée. Cela bloque uniquement pendant que vous attendez un permis de connexion et ne lève pas d'exception si la création d'une nouvelle connexion prend beaucoup de temps. Par défaut, 30000, qui correspond à 30 secondes. |
idle-timeout-minutes |
La durée maximale, en minutes, avant qu'une connexion inactive soit fermée. La durée maximale réelle dépend de la durée d'analyse de l'idleRemover, qui correspond à la moitié du plus petit
idle-timeout-minutes de n'importe quel pool.
|
set-tx-query-timeout |
Indique si on doit définir le timeout d'interrogation par rapport au temps qui reste avant le timeout de transaction. Si aucune transaction n'existe, on utilisera le timeout de recherche qui a été configuré. La valeur par défaut est
false .
|
query-timeout | Timeout pour les recherches, en secondes. La valeur par défaut est « no timeout ». |
allocation-retry | Le nombre de tentatives de connexions avant d'envoyer une connexion. La valeur par défaut est 0 , pour qu'une exception puisse être envoyée à la première défaillance. |
allocation-retry-wait-millis |
Le temps, en millisecondes, qu'il faut attendre avant de retenter d'allouer une connexion. La valeur par défaut est 5 000, soit 5 secondes.
|
xa-resource-timeout |
Si la valeur est non nulle, elle passe à la méthode
XAResource.setTransactionTimeout .
|
Tableau 11.9. Paramètres d'instruction
Paramètre | Description |
---|---|
track-statements |
Indique si l'on doit vérifier les instructions non fermées lorsqu'une connexion est renvoyée à un pool ou qu'une instruction est retournée dans le cache d'instruction préparée. Si false, les instructions ne seront pas suivies.
Valeurs valides
|
prepared-statement-cache-size | Le nombre d'instructions préparées par connexion, dans le cache LRU (le moins souvent utilisé récemment). |
share-prepared-statements |
Indique si le fait de demander la même instruction deux fois sans la fermer utilise la même instruction préparée sous-jacente. La valeur par défaut est
false .
|
Tableau 11.10. Paramètres de recouvrement
Paramètre | Description |
---|---|
recover-credential | Une paire nom d'utilisateur/mot de passe ou domaine de sécurité pour le recouvrement. |
recover-plugin |
Une implémentation de la classe
org.jboss.jca.core.spi.recoveryRecoveryPlugin à utiliser pour le recouvrement.
|
11.6.3. Journalisation des transactions
11.6.3.1. Messages de journalisation de transactions
DEBUG
pour le logger de transaction. Pour un débogage détaillé, utiliser le niveau de journalisation TRACE
. Veuillez consulter Section 11.6.3.2, « Configurer la journalisation des sous-systèmes de transactions » pour plus d'informations sur la configuration du logger de transaction.
TRACE
. Vous trouverez ci-dessous quelques-uns des messages les plus courants. Cette liste n'est pas exhaustive, il se peut que vous rencontriez d'autres messages.
Tableau 11.11. Changement d'état de transaction
Début de transaction |
Quand une transaction commence, le code suivant s'exécute :
com.arjuna.ats.arjuna.coordinator.BasicAction::Begin:1342 tsLogger.logger.trace("BasicAction::Begin() for action-id "+ get_uid()); |
Validation de transaction |
Quand une transaction est validée, le code suivant s'exécute :
com.arjuna.ats.arjuna.coordinator.BasicAction::End:1342 tsLogger.logger.trace("BasicAction::End() for action-id "+ get_uid()); |
Restauration de transaction |
Quand une transaction est restaurée, le code suivant s'exécute :
com.arjuna.ats.arjuna.coordinator.BasicAction::Abort:1575 tsLogger.logger.trace("BasicAction::Abort() for action-id "+ get_uid()); |
Délai d'expiration de transaction |
Quand une transaction expire, le code suivant s'exécute :
com.arjuna.ats.arjuna.coordinator.TransactionReaper::doCancellations:349 tsLogger.logger.trace("Reaper Worker " + Thread.currentThread() + " attempting to cancel " + e._control.get_uid());
Vous verrez ensuite le même thread restaurer la transaction comme montré ci-dessus.
|
11.6.3.2. Configurer la journalisation des sous-systèmes de transactions
Utiliser cette procédure pour contrôler la quantité d'informations enregistrées sur les transactions, indépendamment des autres paramètres de journalisation dans JBoss EAP 6. La procédure montre comment procéder dans la console de gestion sur le web. La commande de gestion CLI est donnée par la suite.
Procédure 11.4. Configurer l'enregistreur de transactions (Transaction Logger) par la console de gestion
Naviguer vers la zone de configuration de la journalisation
Dans la console de gestion, cliquer sur l'onglet Configuration. Si vous utilisez un domaine géré, fermer le profil du serveur que vous souhaitez configurer, à partir de la case de sélection Profile qui se trouve en haut et à gauche.Étendre le menu Core, et sélectionner Logging.Modifier les attributs de
com.arjuna
.Sélectionner l'onglet Log Categories. Sélectionnercom.arjuna
, puis Edit dans Details. Vous pourrez ajouter ici les informations de journalisation spécifiques à la classe. La classecom.arjuna
est déjà présente. Vous pourrez modifier le niveau de journalisation et décider si vous souhaitez utiliser les gestionnaires parents.- Niveau de journalisation
- Le niveau de journalisation est
WARN
par défaut. Comme les transactions peuvent produire une grande quantité de messages de journalisation, la signification des niveaux de journalisation standard est légèrement différente pour l'enregistreur de transactions. En général, les messages avec des niveaux de gravité moins élevés que le niveau choisi seront ignorés.Niveaux de journalisation des transactions, du plus au moins détaillé.
- TRACE
- DEBOG
- INFO
- AVERTISSEMENT
- ERREUR
- FAILURE
- Utiliser les gestionnaires parents
- Indique si l'enregistreur d'événements doit envoyer ses sorties vers l'enregistreur d'événements parent. Le comportement par défaut est
true
.
- Les changements prennent effet immédiatement.
11.6.3.3. Naviguer et gérer les transactions
log-store
. Une opération API nommée probe
lit les journaux de transactions et crée un noeud pour chaque journal. Vous pouvez invoquer la commande probe
manuellement, quand vous souhaitez réactualiser le log-store
. Il est normal pour les journaux de transaction d'apparaitre ou de disparaitre rapidement.
Exemple 11.2. Réactualiser le log store
default
dans un domaine géré. Dans le cas d'un serveur autonome, supprimer profile=default
de la commande.
/profile=default/subsystem=transactions/log-store=log-store/:probe
Exemple 11.3. Voir toutes les transactions préparées
ls
de système de fichiers.
ls /profile=default/subsystem=transactions/log-store=log-store/transactions
Gérer une transaction
- Voir des attributs de transaction.
- Pour voir des informations sur une transaction, comme son nom JNDI, son nom de produit EIS ou sa version, ou encore son statut, utiliser la commande CLI
:read-resource
./profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:read-resource
- Voir tous les participants à une transaction.
- Chaque journal de transaction contient un élément enfant nommé
participants
. Utiliser la commande CLIread-resource
sur cet élément pour voir les participants des transactions. Les participants sont identifiés par leur nom JNDI./profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=java\:\/JmsXA:read-resource
Le résultat doit ressembler à ceci :{ "outcome" => "success", "result" => { "eis-product-name" => "HornetQ", "eis-product-version" => "2.0", "jndi-name" => "java:/JmsXA", "status" => "HEURISTIC", "type" => "/StateManager/AbstractRecord/XAResourceRecord" } }
Le statut du résultat affiché ici est dans un étatHEURISTIC
et est susceptible d'être recouvré. Voir Recouvrement d'une transaction. pour plus d'informations.Dans certains cas, il est possible de créer des enregistrements orphelins dans l'object store, c-a-d XAResourceRecords, qui n'a pas d'enregistrement de transaction correspondante dans le journal. Par exemple, la ressource XA s'était préparée mais avait échoué avant le TM enregistré et est inaccessible à l'API de gestion du domaine. Pour accéder à de tels enregistrements, vous devez définir l'option de gestionexpose-all-logs
àtrue
. Cette option n'est pas sauvegardée dans le modèle de gestion et est restaurée àfalse
quand le serveur redémarre à nouveau./profile=default/subsystem=transactions/log-store=log-store:write-attribute(name=expose-all-logs, value=true)
- Supprimer une transaction.
- Chaque journal de transaction supporte une opération
:delete
pour effacer l'enregistrement qui représente la transaction./profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:delete
- Recouvrement d'une transaction.
- Chaque participant de transaction supporte le recouvrement par la commande CLI
:recover
./profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=2:recover
Recouvrement des transactions heuristiques et des participants
- Si le statut de la transaction est
HEURISTIC
, l'opération de recouvrement change l'état enPREPARE
et déclenche un recouvrement. - Si l'un des participants de la transaction est heuristique, l'opération de recouvrement tente de reprendre l'opération
commit
(validation) à nouveau. En cas de succès, le participant est retiré du journal des transactions. Vous pouvez vérifier cela en exécutant à nouveau l'opération:probe
sur lelog-store
et en vérifiant que le participant n'est plus inscrit. Si c'est le dernier participant, la transaction sera également supprimée.
- Réactualiser le statut de la transaction qui a besoin d'être recouvrée.
- Si une transaction a besoin d'être recouvrée, vous pourrez utiliser la commande CLI
:refresh
pour vous assurer qu'elle a toujours besoin d'être recouvrée, avant de tenter le recouvrement./profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=2:refresh
Si les statistiques de Transaction manager sont activées, vous pouvez consulter les statistiques à propos du gestionnaire de transactions et du sous-système de transaction. Veuillez consulter Section 11.7.8.2, « Configurer le Transaction Manager (TM) (ou gestionnaire de transactions) » pour plus d'informations sur l'activation des statistiques de Transaction manager.
Tableau 11.12. Les statistiques de sous-système de transaction
Statistique | Description | CLI Command |
---|---|---|
Total |
Le nombre total de transactions exécutées par le gestionnaire de transactions sur ce serveur.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-transactions,include-defaults=true) |
Validé |
Le nombre de transactions validées exécutées par le gestionnaire de transactions sur ce serveur.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-committed-transactions,include-defaults=true) |
Abandonné |
Le nombre de transactions interrompues exécutées par le gestionnaire de transactions sur ce serveur.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-aborted-transactions,include-defaults=true) |
Délai expiré |
Le nombre de transactions expirées exécutées par le gestionnaire de transactions sur ce serveur.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-timed-out-transactions,include-defaults=true) |
Heuristiques |
Pas disponible dans la console de gestion. Nombre de transactions dans un état heuristique.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-heuristics,include-defaults=true) |
Transactions In-Flight |
Pas disponible dans la console de gestion. Nombre de transactions commencées non achevées.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-inflight-transactions,include-defaults=true) |
Origine de l'échec - Applications |
Le nombre de transactions échouées dont l'origine de l'échec était une application.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-application-rollbacks,include-defaults=true) |
Origine de l'échec - Ressources |
Le nombre de transactions échouées dont l'origine de l'échec était une ressource.
| /host=master/server=server-one/subsystem=transactions/:read-attribute(name=number-of-resource-rollbacks,include-defaults=true) |
ID Participant |
L'ID du participant.
| /host=master/server=server-one/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:read-children-names(child-type=participants) |
Liste de toutes les transactions |
La liste complète des transactions.
| /host=master/server=server-one/subsystem=transactions/log-store=log-store:read-children-names(child-type=transactions) |
11.7. Utiliser les transactions JTA
11.7.1. Transactions JTA
Les procédures suivantes sont utiles quand vous aurez besoin d'utiliser des transactions dans votre application.
11.7.2. Transactions de contrôle
Cette liste de procédures expose les différentes façons de contrôler les transactions dans vos applications utilisant les API JTA ou JTS.
11.7.3. Démarrer une transaction
Obtenez une instance de
UserTransaction
.Vous pouvez obtenir une instance par JNDI, injection, ou un contexte d'EJB, si l'EJB utilise des transactions gérées-bean, par le biais de l'annotation@TransactionManagement(TransactionManagementType.BEAN)
.JNDI
new InitialContext().lookup("java:comp/UserTransaction")
Injection
@Resource UserTransaction userTransaction;
Contexte
- Pour un bean stateless/stateful :
@Resource SessionContext ctx; ctx.getUserTransaction();
- Pour un bean message-driven :
@Resource MessageDrivenContext ctx; ctx.getUserTransaction()
Appeler
UserTransaction
.begin()
une fois connecté à votre source de données.... try { System.out.println("\nCreating connection to database: "+url); stmt = conn.createStatement(); // non-tx statement try { System.out.println("Starting top-level transaction."); userTransaction.begin(); stmtx = conn.createStatement(); // will be a tx-statement ... } }
L'un des avantages des EJB (qu'ils soient utilisés avec CMT ou BMT) est que le conteneur gère tous les aspects internes de traitement des transactions, c'est à dire que vous êtes libre de vous occuper de transactions faisant partie de transactions XA ou de distribution de transactions dans les conteneurs EAP.
La transaction est lancée. Toute utilisation de votre source de données sera transactionnelle jusqu'à ce que vous validiez ou annuliez la transaction.
Note
11.7.4. Transactions imbriquées
11.7.5. Valider une transaction
Vous devez démarrer une transaction avant de pouvoir la valider. Pour plus d'informations sur le démarrage d'une transaction, veuillez consulter Section 11.7.3, « Démarrer une transaction ».
Appeler la méthode
commit()
surUserTransaction
.Lorsque vous appelez la méthodecommit()
surUserTransaction
, le gestionnaire de transactions tente de valider la transaction.@Inject private UserTransaction userTransaction; public void updateTable(String key, String value) EntityManager entityManager = entityManagerFactory.createEntityManager(); try { userTransaction.begin(): <!-- Perform some data manipulation using entityManager --> ... // Commit the transaction userTransaction.commit(); } catch (Exception ex) { <!-- Log message or notify Web page --> ... try { userTransaction.rollback(); } catch (SystemException se) { throw new RuntimeException(se); } throw new RuntimeException(e); } finally { entityManager.close(); } }
Si vous utilisez des CMT (Container Managed Transactions), vous n'avez pas besoin de valider manuellement.
Si vous configurez votre bean pour qu'il puisse utiliser des transactions gérées conteneur, le conteneur devra gérer le cyle de vie des transactions pour vous sur la base d'annotations que vous aurez configurées dans le code.@PersistenceContext private EntityManager em; @TransactionAttribute(TransactionAttributeType.REQUIRED) public void updateTable(String key, String value) <!-- Perform some data manipulation using entityManager --> ... }
Votre source de données valide et votre transaction se termine, ou une exception est lancée.
Note
11.7.6. Annuler une transaction
Vous devez démarrer une transaction avant de pouvoir la supprimer. Pour plus d'informations sur le démarrage d'une transaction, veuillez consulter Section 11.7.3, « Démarrer une transaction ».
Appeler la méthode
rollback()
surUserTransaction
.Lorsque vous appelez la méthoderollback()
surUserTransaction
, le gestionnaire de transactions tentera de valider la transaction.@Inject private UserTransaction userTransaction; public void updateTable(String key, String value) EntityManager entityManager = entityManagerFactory.createEntityManager(); try { userTransaction.begin(): <!-- Perform some data manipulation using entityManager --> ... // Commit the transaction userTransaction.commit(); } catch (Exception ex) { <!-- Log message or notify Web page --> ... try { userTransaction.rollback(); } catch (SystemException se) { throw new RuntimeException(se); } throw new RuntimeException(e); } finally { entityManager.close(); } }
Si vous utilisez des CMT (Container Managed Transactions), vous n'avez pas besoin de supprimer la transaction manuellement.
Si vous configurez votre bean pour qu'il puisse utiliser des transactions gérées conteneur, le conteneur devra gérer le cyle de vie des transactions pour vous sur la base d'annotations que vous aurez configurées dans le code.Note
Une annulation CMT se produit en cas d'exception RuntimeException. Vous pouvez également invoquer la méthodesetRollbackOnly
pour l'annulation ou utiliser@ApplicationException(rollback=true
) pour obtenir une exception d'application pour l'annulation.
Votre transaction est annulée par le gestionnaire de transactions.
Note
11.7.7. Gérer un résultat heuristique dans une transaction
Procédure 11.5. Gérer un résultat heuristique dans une transaction
Déterminer la cause
En général, la cause d'un résultat heuristique pour une transaction est qu'un gestionnaire de ressources a promis qu'il pourrait commettre ou annuler, et il n'a pas pu remplir sa promesse. Cela pouvait être dû à un problème avec un composant de tierce partie, la couche d'intégration entre le composant tiers et la plate-forme EAP 6 ou bien la plate-forme JBoss EAP 6 elle-même.De loin, les deux causes les plus communes d'erreurs heuristiques sont des erreurs temporaires situées dans l'environnement ou des erreurs de code liées aux gestionnaires de ressources.Solution aux échecs temporaires de l'environnement
Normalement, s'il y a un échec temporaire dans votre environnement, vous le saurez avant d'être au courant de l'erreur heuristique. Il peut s'agir d'une défaillance de réseau ou de matériel, d'une erreur de base de données, d'une panne de courant, d'un hôte ou de tout autre cause.En cas de résultat heuristique ayant lieu dans un environnement de test, au cours de test de stress, cela vous fournit des informations sur les points faibles de votre environnement.Avertissement
La plate-forme JBoss EAP 6 restaurera automatiquement les transactions qui ne sont pas dans un état heuristique au moment de la défaillance, mais elle ne tentera pas de restaurer les transactions heuristiques.Contacter les fournisseurs de gestionnaires de ressources
Si vous n'expérimentez aucun échec évident dans votre environnement, ou si le résultat heuristique est facilement reproductible, c'est probablement une erreur de codage. Contacter les fournisseurs de tierce partie pour savoir si une solution est disponible. Si vous pensez que le problème est dans le gestionnaire de transactions de la plate-forme JBoss EAP 6 elle-même, veuillez prendre contact avec Red Hat Global Support Services.Dans un environnement de test, supprimer les logs et démarrez à nouveau la plate-forme JBoss EAP 6.
Dans un environnement de test, ou bien dans les cas où vous ne vous souciez pas de l'intégrité des données, le fait de supprimer les journaux des transactions et de redémarrer la plate-forme JBoss EAP 6 se débarrasse du résultat heuristique. Les journaux des transactions sont situés dansEAP_HOME/standalone/data/tx-object-store/
pour les serveurs autonomes, ou dansEAP_HOME/domain/servers/SERVER_NAME/data/tx-object-store
pour les domaines gérés, par défaut. Dans le cas d'un domaine géré, SERVER_NAME se rapporte au nom du serveur individuel qui participe à un groupe de serveurs.Note
L'emplacement du journal de transactions dépend aussi du store d'objets en usage et des valeurs définies des paramètresoject-store-relative-to
etobject-store-path
. Pour les journaux de système de fichier (journaux en mémoire fantôme standard et les journaux HornetQ), la localisation de direction par défaut est utilisée, mais lorsque vous utilisez un magasin d'objets JDBC, les journaux des transactions sont stockés dans une base de données.Résoudre le résultat à la main
Le processus de résolution de résultat à la main dépend des circonstances particulières de l'incident. Normalement, vous aurez besoin de procéder aux étapes suivantes, en les appliquant à votre situation.- Identifier les gestionnaires de ressources impliqués.
- Examiner l'état dans le gestionnaire de transactions et dans les gestionnaires de ressources.
- Forcer manuellement le nettoyage des journaux et la reconciliation des données dans un ou plusieurs des composants impliqués.
La façon dont procéder à ces étapes va delà de la portée de ce document.
11.7.8. Temps d'expiration des transactions
11.7.8.1. Les délais d'attente des transactions
11.7.8.2. Configurer le Transaction Manager (TM) (ou gestionnaire de transactions)
default
, il se peut que vous ayiez à modifier les étapes et les commandes de la manière suivante.
Notes sur les commandes d'exemple
- Pour la console de gestion, le profil par défaut
default
est celui qui sera sélectionné quand vous vous connectez. Si vous souhaitez modifier la configuration du gestionnaire de transactions (TM) dans un autre profile, sélectionnez votre profile à la place, et non pasdefault
, pour chaque instruction.De même, substituez votre profil à la place du profil par défautdefault
pour les commandes CLI de l'exemple. - Si vous utilisez un serveur autonome, un seul profil existe. Ignorer toute instruction pour choisir un profil spécifique. Dans les commandes CLI, retirer la partie
/profile=default
des commandes d'échantillon.
Note
transactions
doit être activé. Il est activé par défaut, et il faut pour cela qu'un certain nombre d'autres sous-systèmes fonctionnent correctement, donc il est improbable qu'il soit désactivé.
Pour configurer le gestionnaire de transactions (TM) à l'aide de la console de gestion sur le web, sélectionnez l'onglet Configuration en haut de l'écran. Si vous utilisez un domaine géré, vous avez le choix de plusieurs profils. Choisir le bon Profile de la boîte de sélection dans la partie supérieure gauche de l'écran. Étendez le menu Container, et sélectionnez Transactions.
Dans l'interface CLI, vous pouvez configurer le TM en utilisant une série de commandes. Les commandes commencent toutes par /profile=default/subsystem=transactions/
pour un domaine géré avec default
de profil, ou par /subsystem=transactions
pour un serveur autonome.
Important
Tableau 11.13. Options de configuration du TM
Option | Description | CLI Command |
---|---|---|
Activer les statistiques
|
Indique s'il faut activer les statistiques de transaction. Ces statistiques se trouvent dans la console de gestion dans la section Subsystem Metrics de l'onglet Runtime.
| /profile=default/subsystem=transactions/:write-attribute(name=enable-statistics,value=true)
|
Activer le statut TSM
|
Indique si l'on doit activer le service de gestion du statut de transaction (TSM), qui est utilisé pour le recouvrement hors-processus. L'exécution d'un manager de recouvrement de processus pour contacter l'ActionStatusService à partir de processus différents n'est pas pris en charge (normalement contacté en mémoire).
|
Cette option de configuration n'est pas prise en charge.
|
Délai d'attente par défaut
|
Délai d'attente de transaction par défaut. La valeur par défaut est de
300 secondes. Vous pouvez la remplacer par programmation, sur la base d'une transaction.
| /profile=default/subsystem=transactions/:write-attribute(name=default-timeout,value=300)
|
Chemin de Store Objet
|
Un chemin de système de fichiers relatif ou absolu où le store objet TM stocke des données. Relatif, par défaut, à la valeur du paramètre
object-store-relative-to .
| /profile=default/subsystem=transactions/:write-attribute(name=object-store-path,value=tx-object-store)
|
Chemin de Store Objet Relatif à
|
Référence une configuration de chemin global dans le modèle du domaine. La valeur par défaut correspond au répertoire de données de JBoss EAP 6, qui correspond à la valeur de la propriété
jboss.server.data.dir , et qui a pour valeur par défaut EAP_HOME/domain/data/ pour un domaine géré, ou EAP_HOME/standalone/data/ pour une instance de serveur autonome. La valeur de l'attribut object-store-path de l'object store est relative à ce chemin.
| /profile=default/subsystem=transactions/:write-attribute(name=object-store-relative-to,value=jboss.server.data.dir)
|
Liaisons de sockets
|
Indique le nom de la liaison du socket utilisé par le gestionnaire de transactions pour la récupération et la création des identificateurs de transactions, lorsque le mécanisme du socket est utilisé. Se référer à
processus-id-socket-max-ports pour plus d'informations sur la génération de l'identificateur unique. Les liaisons de socket sont spécifiées par le groupe de serveurs dans l'onglet Serveur de la console de gestion.
| /profile=default/subsystem=transactions/:write-attribute(name=socket-binding,value=txn-recovery-environment)
|
Statut de liaison de socket
|
Indique la liaison de socket à utiliser pour le gestionnaire de statuts de transactions.
|
Cette option de configuration n'est pas prise en charge.
|
Listener de recouvrement
|
Indique si oui ou non le processus de recouvrement de transactions doit écouter au socket de réseau. La valeur par défaut est
false .
| /profile=default/subsystem=transactions/:write-attribute(name=recovery-listener,value=false)
|
Tableau 11.14. Options de configuration du TM avancées
Option | Description | CLI Command |
---|---|---|
jts
|
Indique si l'on doit utiliser les transactions Java Transaction Service (JTS). La valeur par défaut est
false , qui utilise des transactions JTA uniquement.
| /profile=default/subsystem=transactions/:write-attribute(name=jts,value=false)
|
Identificateur de nœud
|
L'identificateur de noeud de gestionnaire de transactions. Cette option est requise dans les situations suivantes :
node-identifier doit être unique pour chaque gestionnaire de transaction car il doit assurer l'intégrité des données pendant le recouvrement. Le node-identifier doit également être unique à JTA car plusieurs noeuds peuvent entrer en interraction avec le même gestionnaire de ressources ou partager un object store de transactions.
| /profile=default/subsystem=transactions/:write-attribute(name=node-identifier,value=1)
|
process-id-socket-max-ports
|
Le gestionnaire de transactions crée un identifiant unique pour chaque journal des transactions. Deux mécanismes différents sont fournis pour générer des identificateurs uniques : un mécanisme basé sur le socket et un mécanisme fondé sur l'identificateur de processus du processus.
Dans le cas de l'identifiant basé-socket, le socket est ouvert et son numéro de port est utilisé pour l'identifiant. Si le port est déjà utilisé, on cherchera le port suivant jusqu'à ce qu'un port libre soit trouvé. Les
processus-id-socket-max-ports représentent le nombre maximal de sockets que le TM va essayer avant d'abandonner. La valeur par défaut est 10 .
| /profile=default/subsystem=transactions/:write-attribute(name=process-id-socket-max-ports,value=10)
|
process-id-uuid
|
Définir à
true avec un identifiant de processus pour créer un identifiant unique pour chaque transaction. Sinon, le mécanisme basé socket sera utilisé. La valeur par défaut est true . Se référer à process-id-socket-max-ports pour obtenir davantage d'informations. Pour activer process-id-socket-binding , définir process-id-uuid à false .
| /profile=default/subsystem=transactions/:write-attribute(name=process-id-uuid,value=true)
|
process-id-socket-binding
|
Le nom de la configuration de liaison de socket à utiliser si le gestionnaire de transactions doit utiliser un id de processus basé-socket. Correspondra à
undefined si process-id-uuid est sur true ; sinon il devra être défini.
| /profile=default/subsystem=transactions/:write-attribute(name=process-id-socket-binding,value=true)
|
use-hornetq-store
|
Utiliser les mécanismes de stockage journalisés de HornetQ au lieu du stockage basé sur des fichiers, pour les journaux de transactions. Ceci est désactivé par défaut, mais peut améliorer les performances I/O. Il n'est pas recommandé pour les transactions JTS sur les gestionnaires de transactions séparés. Quand vous changez cette option, le serveur doit démarrer à nouveau à l'aide de la commande
shutdown pour que le changement puisse prendre effet.
| /profile=default/subsystem=transactions/:write-attribute(name=use-hornetq-store,value=false)
|
11.7.9. Gestion des erreurs de transactions JTA
11.7.9.1. Erreurs de transactions
Note
Ce type d'erreur se manifeste souvent quand Hibernate n'est pas en mesure d'obtenir une connexion de base de données pour le chargement différé. Si cela survient fréquemment, vous pourrez augmenter la valeur du temps d'expiration. Voir Section 11.7.8.2, « Configurer le Transaction Manager (TM) (ou gestionnaire de transactions) ».
NotSupportedException
L'exception NotSupportedException
indique habituellement que vous avez tenté d'insérer une transaction JTA, et que ce n'est pas pris en charge. Si vous n'étiez pas en train d'essayer d'insérer une transaction, il est probable qu'une autre transaction avait commencé dans une tâche de pool de threads, mais a terminé la tâche sans suspendre, ni mettre fin à la transaction.
UserTransaction
, qui s'en occupe automatiquement. Si tel est le cas, il y a peut-être un problème dans le framework.
TransactionManager
ou Transactions
directement, considérez le comportement suivant lors de la validation ou de l'annulation d'une transaction. Si votre code utilise les méthodes du TransactionManager
pour contrôler vos transactions, la validation ou l'annulation d'une transaction dissociera la transaction du thread actuel. Toutefois, si votre code utilise les méthodes Transaction
, la transaction peut ne pas être associée au thread en cours d'exécution, et il faudra la dissocier de son thread manuellement, avant de la retourner au pool de threads.
Cette erreur a lieu si vous essayez d'enlister une seconde ressource non-XA dans une transaction. Si vous avez besoin de ressources multiples pour une transaction, elles doivent être XA.
11.8. Configuration ORB
11.8.1. CORBA (Common Object Request Broker Architecture)
11.8.2. Configurer l'ORB pour les transactions JTS
Note
full
et full-ha
uniquement. Dans un serveur autonome, il est disponible uniquement quand vous utilisez les configurations standalone-full.xml
ou standalone-full-ha.xml
.
Procédure 11.6. Configurer l'ORB par la console de gestion
Voir les paramètres de configuration du profil.
Sélectionner Configuration dans la partie supérieure de la console de gestion. Si vous utilisez un domaine géré, sélectionner soit le profil full ou full-ha à partir de la boîte de dialogue de sélection en haut à gauche.Modifier les paramètres Initializers
Étendre sur le menu Subsystems. Étendre le menu Container, et sélectionner JacORB.Sur le formulaire qui apparaît sur l'écran principal, sélectionner l'onglet Initializers, et cliquer sur le bouton Edit.Activer les intercepteurs de sécurité en configurant la valeur de Security àactive
.Pour activer ORB sur JTS, définir la valeur des Transaction Interceptorss àactive
, au lieu de la valeur par défautspec
.Voir le lien Need Help? sur le formulaire pour accéder à des explications sur ces valeurs. Cliquer sur Save quand vous aurez fini de modifier les valeurs.Configuration ORB avancée
Voir les autres sections du formulaire pour les options de configuration avancées. Chaque section inclut un lien Need Help? avec des informations détaillées sur les paramètres.
Vous pouvez configurer chaque aspect de l'ORB à l'aide de l'interface CLI. Les commandes suivantes configurent les initialisateurs aux mêmes valeurs que celles de la procédure ci-dessus, pour la console de gestion. Il s'agit de la configuration minimale pour l'ORB, si utilisé avec JTS.
/profile=full
des commandes.
Exemple 11.4. Activer les intercepteurs de sécurité
/profile=full/subsystem=jacorb/:write-attribute(name=security,value=on)
Exemple 11.5. Activer les transactions dans le sous-système JacORB
/profile=full/subsystem=jacorb/:write-attribute(name=transactions,value=on)
Exemple 11.6. Activer JTS dans le sous-système de transactions
/profile=full/subsystem=transactions:write-attribute(name=jts,value=true)
Note
11.9. Références de transactions
11.9.1. Erreurs et exceptions pour les transactions JBoss
UserTransaction
, voir la spécification UserTransaction API dans http://docs.oracle.com/javaee/6/api/javax/transaction/UserTransaction.html.
11.9.2. Exemple de transaction JTA
Exemple 11.7. Exemple de transaction JTA
public class JDBCExample { public static void main (String[] args) { Context ctx = new InitialContext(); // Change these two lines to suit your environment. DataSource ds = (DataSource)ctx.lookup("jdbc/ExampleDS"); Connection conn = ds.getConnection("testuser", "testpwd"); Statement stmt = null; // Non-transactional statement Statement stmtx = null; // Transactional statement Properties dbProperties = new Properties(); // Get a UserTransaction UserTransaction txn = new InitialContext().lookup("java:comp/UserTransaction"); try { stmt = conn.createStatement(); // non-tx statement // Check the database connection. try { stmt.executeUpdate("DROP TABLE test_table"); stmt.executeUpdate("DROP TABLE test_table2"); } catch (Exception e) { // assume not in database. } try { stmt.executeUpdate("CREATE TABLE test_table (a INTEGER,b INTEGER)"); stmt.executeUpdate("CREATE TABLE test_table2 (a INTEGER,b INTEGER)"); } catch (Exception e) { } try { System.out.println("Starting top-level transaction."); txn.begin(); stmtx = conn.createStatement(); // will be a tx-statement // First, we try to roll back changes System.out.println("\nAdding entries to table 1."); stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)"); ResultSet res1 = null; System.out.println("\nInspecting table 1."); res1 = stmtx.executeQuery("SELECT * FROM test_table"); while (res1.next()) { System.out.println("Column 1: "+res1.getInt(1)); System.out.println("Column 2: "+res1.getInt(2)); } System.out.println("\nAdding entries to table 2."); stmtx.executeUpdate("INSERT INTO test_table2 (a, b) VALUES (3,4)"); res1 = stmtx.executeQuery("SELECT * FROM test_table2"); System.out.println("\nInspecting table 2."); while (res1.next()) { System.out.println("Column 1: "+res1.getInt(1)); System.out.println("Column 2: "+res1.getInt(2)); } System.out.print("\nNow attempting to rollback changes."); txn.rollback(); // Next, we try to commit changes txn.begin(); stmtx = conn.createStatement(); ResultSet res2 = null; System.out.println("\nNow checking state of table 1."); res2 = stmtx.executeQuery("SELECT * FROM test_table"); while (res2.next()) { System.out.println("Column 1: "+res2.getInt(1)); System.out.println("Column 2: "+res2.getInt(2)); } System.out.println("\nNow checking state of table 2."); stmtx = conn.createStatement(); res2 = stmtx.executeQuery("SELECT * FROM test_table2"); while (res2.next()) { System.out.println("Column 1: "+res2.getInt(1)); System.out.println("Column 2: "+res2.getInt(2)); } txn.commit(); } catch (Exception ex) { ex.printStackTrace(); System.exit(0); } } catch (Exception sysEx) { sysEx.printStackTrace(); System.exit(0); } } }
11.9.3. Documentation API pour JBoss Transactions JTA
11.9.4. Limitations dans le processus de recouvrement XA
- Le journal de transactions
- Si le serveur JBoss EAP échoue après qu'une méthode de validation
XAResource
réussisse et validifie la transaction, mais avant que le coordinateur puisse mettre le journal à jour, vous verrez sans doute le message suivant apparaître dans le journal quand vous démarrerez le serveur à nouveau :ARJUNA016037: Could not find new XAResource to use for recovering non-serializable XAResource XAResourceRecord
C'est parce que lors de la restauration, le gestionnaire de transactions de JBoss (JBoss Transaction Manager) voit les participants à la transaction dans le journal et tente la validation à nouveau. Ensuite, le gestionnaire de transactions de JBoss suppose que les ressources sont validées et ne tente plus de valider à nouveau. Dans ce cas, il est possible d'ignorer cet avertissement car la transaction est validée et il n'y a aucune perte de données.Pour éviter cet avertissement, définir la valeur de la propriété com.arjuna.ats.jta.xaAssumeRecoveryComplete àtrue
. Cette propriété est vérifiée à chaque fois qu'une nouvelle instance deXAResource
ne peut pas être trouvée parmi lesXAResourceRecovery
enregistrées. Si défini surtrue
, le recouvrement assume que les tentatives précédentes ont réussi, et l'instance peut être supprimée du journal sans besoin de nouvelle tentative. Cette propriété doit être utilisée avec soin car elle est globale et si elle est utilisée par de façon erronée, on se retrouve avec des instancesXAResource
restantes non validées. - La restauration n'est pas automatique pour les transactions JTS qui suivent un plantage de serveur à la fin de XAResource.prepare().
- Si le serveur JBoss EAP se plante suite à un appel de méthode
XAResource
prepare()
, tous les participants à XAResources sont verrouillés dans l'état « prepared » et y demeurent lors du redémarrage du serveur. La transaction n'est pas restaurée et les ressources restent verrouillées jusqu'à ce que l'opération arrive à expiration ou qu'un DBA restaure les ressources et efface les transactions du journal manuellement. - Le recouvrement périodique peut avoir lieu sur des transactions déjà validées.
- Quand le serveur est sous charge intensive, le journal du serveur peut contenir les messages d'avertissement suivants, suivis d'un stackstace :
ARJUNA016027: Local XARecoveryModule.xaRecovery got XA exception XAException.XAER_NOTA: javax.transaction.xa.XAException
Sous forte charge, le temps de traitement pris par une transaction peut chevaucher le calendrier d'activité de la procédure de restauration périodique. Le processus de restauration périodique détecte la transaction en cours d'exécution et essaie d'initier une restauration, mais en fait, la transaction se poursuit jusqu'à la fin. Au moment où le processus de restauration périodique tente mais échoue à la restauration, il enregistre l'échec de restauration dans le journal de serveur. La cause sous-jacente de cette question sera abordée dans une prochaine version, mais en attendant, une solution de contournement est disponible.Augmenter l'intervalle entre les deux phases du processus de restauration en définissant la propriété com.arjuna.ats.jta.orphanSafetyInterval sur une valeur supérieure à la valeur par défaut de 10 000 millisecondes. Une valeur de 40 000 millisecondes est recommandée. Veuillez noter que cela ne résout pas le problème, au contraire, cela diminue la probabilité qu'il se produira et que le message d'avertissement s'affichera dans le journal.
Chapitre 12. Hibernate
12.1. Hibernate Core
12.2. Java Persistence API (JPA)
12.2.1. JPA
bean-validation
, greeter
, et kitchensink
: Section 1.4.1.1, « Accès aux Quickstarts ».
12.2.2. Hibernate EntityManager
12.2.3. Guide de départ
12.2.3.1. Créer un projet JPA dans le Red Hat JBoss Developer Studio
Cet exemple indique les étapes à effectuer pour créer un projet JPA dans Red Hat JBoss Developer Studio.
Procédure 12.1. Créer un projet JPA dans le Red Hat JBoss Developer Studio
- Dans la fenêtre Red Hat JBoss Developer Studio, cliquez sur File → New → Project. Trouvez JPA dans la liste, étendez-la et sélectionnez JPA Project. La boîte de dialogue suivante s'affichera.
- Saisissez un Project name.
- Sélectionnez un Target runtime. Si aucun target runtime n'est disponible, suivez ces instructions pour définir un nouveau serveur et runtime : Section 1.3.1.5, « Ajouter le serveur de JBoss EAP en utilisant Define New Server ».
- Sous JPA version, assurez-vous que 2.1 est sélectionné.
- Sous Configuration, choisissez Basic JPA Configuration.
- Cliquer sur Terminé.
- Si cela vous est demandé, choisissez si vous souhaitez associer ce type de projet à la fenêtre de perspective JPA.
12.2.3.2. Créer le fichier de paramètres de persistance dans Red Hat JBoss Developer Studio
Cette section couvre le processus de création du fichier persistence.xml
dans un projet Java qui utilise Red Hat JBoss Developer Studio.
Conditions préalables
Procédure 12.2. Créer et configurer un nouveau fichier de configuration de persistance
- Ouvrir un projet EJB 3.x dans Red Hat JBoss Developer Studio.
- Cliquer à droite sur le répertoire racine du projet dans le panneau Project Explorer
- Sélectionner Nouveau → Autre....
- Sélectionner XML File à partir du dossier XML et cliquer sur Next.
- Sélectionner le dossier
ejbModule/META-INF
comme répertoire parent. - Nommer le fichier
persistence.xml
et cliquer sur Suivant. - Sélectionner Create XML file from an XML schema file (créer fichier à partir d'un fichier xml) et cliquer sur Next.
- Sélectionner http://java.sun.com/xml/ns/persistence/persistence_2.0.xsd dans la liste Select XML Catalog entry et cliquer sur Next.
- Cliquer sur Terminer pour créer le fichier.
- Résultat :
- Le fichier
persistence.xml
a été créé dans le dossierMETA-INF/
et est prêt pour la configuration. Un exemple de fichier se trouve ici : Section 12.2.3.3, « Exemple de fichier de configuration de persistance »
12.2.3.3. Exemple de fichier de configuration de persistance
Exemple 12.1. persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="example" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <mapping-file>ormap.xml</mapping-file> <jar-file>TestApp.jar</jar-file> <class>org.test.Test</class> <shared-cache-mode>NONE</shared-cache-mode> <validation-mode>CALLBACK</validation-mode> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit> </persistence>
12.2.3.4. Créer le fichier de configuration Hibernate dans Red Hat JBoss Developer Studio
Conditions préalables
Ce sujet couvre le processus de création du fichier hibernate.cfg.xml
dans un projet Java qui utilise Red Hat JBoss Developer Studio.
Procédure 12.3. Créer un nouveau fichier de configuration Hibernate
- Ouvrir un projet Java dans Red Hat JBoss Developer Studio
- Cliquer à droite sur le répertoire racine du projet dans le panneau Project Explorer
- Sélectionner New → Other....
- Sélectionner Hibernate Configuration File depuis le dossier Hibernate et cliquer sur Next.
- Sélectionner le répertoire
src/
et cliquer sur Next. - Configurer :
- Nom de la fabrique à sessions
- Dialecte de base de données
- Classe pilote
- URL de connexion
- Nom d'utilisateur
- Mot de passe
- Cliquer sur Finish pour créer le fichier.
- Résultat :
- Le fichier
hibernate.cfg.xml
a été créé dans le dossiersrc/
. Un exemple de fichier est disponible ici : Section 12.2.3.5, « Exemple de fichier de configuration Hibernate ».
12.2.3.5. Exemple de fichier de configuration Hibernate
Exemple 12.2. hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Datasource Name --> <property name="connection.datasource">ExampleDS</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.H2Dialect</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.region.factory_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Update the database schema on startup --> <property name="hbm2ddl.auto">update</property> <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/> </session-factory> </hibernate-configuration>
12.2.4. Configuration
12.2.4.1. Propriétés de configuration de Hibernate
Tableau 12.1. Propriétés
Nom de propriété | Description |
---|---|
hibernate.dialect |
Le nom de classe d'un
org.hibernate.dialect.Dialect Hibernate. Permet à Hibernate de générer SQL optimisé pour une base de données relative particulière.
Dans la plupart des cas, Hibernate pourra choisir la bonne implémentation de
org.hibernate.dialect.Dialect , selon les JDBC metadata renvoyées par le pilote JDBC.
|
hibernate.show_sql |
Booléen. Écrit toutes les déclarations SQL à la console. C'est une alternative pour définir la catégorie du journal
org.hibernate.SQL sur debug .
|
hibernate.format_sql |
Booléen. Mettre en forme le SQL dans le journal et la console.
|
hibernate.default_schema |
Qualifie les noms de tableaux non qualifiés avec le schéma/espace tableau donné dans le SQL généré.
|
hibernate.default_catalog |
Qualifie les noms de tableaux non qualifiés avec le catalogue donné dans le SQL généré.
|
hibernate.session_factory_name |
Le
org.hibernate.SessionFactory sera automatiquement lié à ce nom dans JNDI après avoir été créé. Par exemple, jndi/composite/name .
|
hibernate.max_fetch_depth |
Définit une "profondeur" maximum pour l'arborescence de récupération de la jointure externe pour les associations à fin unique (un-à-un, plusieurs-à-un). Un
0 désactive la récupération de jointure externe par défaut. La valeur recommandée se situe entre 0 et 3 .
|
hibernate.default_batch_fetch_size |
Définit une taille par défaut pour la récupération en lot d'associations de Hibernate. Les valeurs recommandées sont
4 , 8 , et 16 .
|
hibernate.default_entity_mode |
Définit un mode par défaut pour la réprésentation d'entité pour toutes les sessions ouvertes à partir de
SessionFactory . Les valeurs comprennent : dynamic-map , dom4j , pojo .
|
hibernate.order_updates |
Booléen. Oblige Hibernate à ordonner les mises à jour SQL par la valeur clé principale des éléments mis à jour. Cela se traduira en un nombre inférieur de blocages de transactions dans des systèmes hautement simultanés.
|
hibernate.generate_statistics |
Booléen. Si autorisé, Hibernate va collecter les statistiques utiles pour le réglage de performance.
|
hibernate.use_identifier_rollback |
Booléen. Si autorisé, les propriétés d'identifiant générées seront restaurées à leur valeur par défaut lorsque les objets sont supprimés.
|
hibernate.use_sql_comments |
Booléen. Si activé, Hibernate génèrera des commentaires à l'intérieur de SQL, pour faciliter de débogage. La valeur par défaut est
false .
|
hibernate.id.new_generator_mappings |
Booléen. Cette propriété est appropriée lorsque
@GeneratedValue est utilisée. Elle indique si les nouvelles implémentations IdentifierGenerator sont utilisées pour javax.persistence.GenerationType.AUTO , javax.persistence.GenerationType.TABLE et javax.persistence.GenerationType.SEQUENCE . La valeur par défaut est true .
|
hibernate.ejb.naming_strategy |
Sélectionne l'implémentation
org.hibernate.cfg.NamingStrategy avec Hibernate EntityManager. Cette classe est dépréciée et cette propriété n'est fournie que pour la compatibilité rétroactive. Cette propriété doit être utilisée avec hibernate.ejb.naming_strategy_delegator .
Si l'application n'utilise pas EntityManager, suivre les instructions suivantes pour configurer la NamingStrategy: Hibernate Reference Documentation - Implementing a Naming Strategy.
|
hibernate.ejb.naming_strategy_delegator |
Indique une implémentation
org.hibernate.cfg.naming.NamingStrategyDelegatator pour les objets de base de données et les éléments de schéma avec un Entity Manager Hibernate. Cette propriété peut comporter les valeurs suivantes.
Note
Cette propriété ne peut pas être utilisée avec hibernate.ejb.naming_strategy . C'est un remplacement temporaire de org.hibernate.cfg.NamingStrategy pour pallier à ses limitations. Nous envisageons une solution plus complète pour Hibernate 5.0 qui remplacera à la fois org.hibernate.cfg.NamingStrategy et org.hibernate.cfg.naming.NamingStrategyDelegator .
Si l'application n'utilise pas EntityManager, suivre les instructions suivantes pour configurer la NamingStrategy: Hibernate Reference Documentation - Implementing a Naming Strategy.
|
Important
hibernate.id.new_generator_mappings
, les nouvelles applications devront garder la valeur par défaut true
. Les applications existantes qui utilisent Hibernate 3.3.x devront sans doute modifier cette valeur à false
pour continuer à utiliser un objet de séquence ou un générateur basé-table et pour maintenir la compatibilité rétro-active.
12.2.4.2. Hibernate JDBC et Propriétés de Connexion
Tableau 12.2. Propriétés
Nom de propriété | Description |
---|---|
hibernate.jdbc.fetch_size |
Valeur non nulle qui détermine la taille de récupération JDBC (appelle
Statement.setFetchSize() ).
|
hibernate.jdbc.batch_size |
Une valeur non nulle active l'utilisation des mises à jour de lots de JDBC2 par Hibernate. Les valeurs recommandées se situent entre
5 et 30 .
|
hibernate.jdbc.batch_versioned_data |
Booléen. Définir cette propriété sur
true si le pilote JDBC renvoie des nombres de lignes corrects avec executeBatch() . Hibernate utilisera ensuite un DML en lot pour les données versionnées automatiquement. La valeur par défaut est false .
|
hibernate.jdbc.factory_class |
Sélectionner un
org.hibernate.jdbc.Batcher personnalisé. La plupart des applications n'auront pas besoin de cette propriété de configuration.
|
hibernate.jdbc.use_scrollable_resultset |
Booléen. Active l'utilisation des ensembles de résultats déroulants de JDBC2 par Hibernate. Cette propriété n'est nécessaire que lorsque l'on utilise des connexions JDBC fournies par l'utilisateur. Sans quoi, Hibernate utilise des métadonnées de connexion.
|
hibernate.jdbc.use_streams_for_binary |
Booléen. Ceci est une propriété de niveau système. Utiliser les flux lors de l'écriture/la lecture de types
binary ou serializable depuis/vers JDBC.
|
hibernate.jdbc.use_get_generated_keys |
Booléen. Active l'utilisation de JDBC3
PreparedStatement.getGeneratedKeys() pour récupérer les cléfs générées en mode natif après insertion. Requiert le pilote JDBC3+ et JRE1.4+. Défini sur false si le pilote JDBC rencontre des problèmes avec les générateurs d'identifiants Hibernate. Par défaut, il tente de déterminer les capacités du pilote en utilisant les métadonnées de connexion.
|
hibernate.connection.provider_class |
Le nom de classe d'un
org.hibernate.connection.ConnectionProvider personnalisé qui fournit des connexions JDBC à Hibernate.
|
hibernate.connection.isolation |
Définit le niveau d'isolation des transactions JDBC. Vérifier
java.sql.Connection pour des valeurs compréhensibles, mais veuillez noter que la plupart des bases de données ne prennent pas en charge tous les niveaux d'isolation et que quelques unes définissent des isolations additionnelles, non-standard. Les valeurs standard sont 1, 2, 4, 8 .
|
hibernate.connection.autocommit |
Booléen. Cette propriété n'est pas recommandée à l'utilisation. Active l'autovalidation pour les connexions « en commun » de JDBC.
|
hibernate.connection.release_mode |
Indique à quel moment Hibernate devra diffuser les connexions JDBC. Par défaut, une connexion JDBC est maintenue jusqu'à ce que la session soit explicitement fermée ou déconnectée. La valeur par défaut
auto choisira after_statement pour les stratégies de transaction JTA et CMT, et after_transaction pour la stratégie de transaction JDBC.
Les valeurs disponibles sont
auto (par défaut) | on_close | after_transaction | after_statement .
Ce paramètre n'affecte que les
Sessions renvoyées depuis SessionFactory.openSession . Pour les Session s obtenues par SessionFactory.getCurrentSession , l'implémentation CurrentSessionContext configurée pour l'utilisation contrôle le mode de publication de connexion pour ces Sessions .
|
hibernate.connection.<propertyName> |
Passer la propriété JDBC <propertyName> à
DriverManager.getConnection() .
|
hibernate.jndi.<propertyName> |
Passer la proriété <propertyName> à
InitialContextFactory de JNDI.
|
12.2.4.3. Propriétés d'Hibernate Cache
Tableau 12.3. Propriétés
Nom de propriété | Description |
---|---|
hibernate.cache.region.factory_class |
Le nom de classe d'un
CacheProvider personnalisé.
|
hibernate.cache.use_minimal_puts |
Booléen. Optimise l'opération cache de second niveau pour minimiser les écritures, au détriment de lectures plus fréquentes. Cette configuration est surtout utile pour les caches clusterisés et, dans Hibernate 3, est activée par défaut pour les implémentations cache clusterisées.
|
hibernate.cache.use_query_cache |
Booléen. Active le cache de recherche. Les recherches individuelles doivent toujours être définies comme cachables.
|
hibernate.cache.use_second_level_cache |
Booléen. Utilisé pour désactiver totalement le cache de second niveau, qui est activé par défaut pour les classes qui spécifient un mappage
<cache> .
|
hibernate.cache.query_cache_factory |
Le nom de classe d'une interface
QueryCache personnalisée. La valeur par défaut est le StandardQueryCache intégré.
|
hibernate.cache.region_prefix |
Un préfixe à utiliser pour les noms régionaux de cache de second niveau.
|
hibernate.cache.use_structured_entries |
Booléen. Force Hibernate à stocker des données dans un cache de second niveau dans un format plus amical pour l'utilisateur.
|
hibernate.cache.default_cache_concurrency_strategy |
Configuration utilisée pour donner le nom de la statégie qu'il faut
org.hibernate.annotations.CacheConcurrencyStrategy quand @Cacheable ou @Cache sont utilisés. @Cache(strategy="..") est utilisé pour remplacer cette valeur par défaut.
|
12.2.4.4. Propriétés de transaction Hibernate
Tableau 12.4. Propriétés
Nom de propriété | Description |
---|---|
hibernate.transaction.factory_class |
Le nom de classe d'une
TransactionFactory à utiliser avec l'API de Transaction de Hibernate. Valeur par défaut : JDBCTransactionFactory .
|
jta.UserTransaction |
Un nom JNDI utilisé par
JTATransactionFactory pour obtenir la UserTransaction JTA depuis le serveur d'applications.
|
hibernate.transaction.manager_lookup_class |
Le nom de classe d'une
TransactionManagerLookup . Requis lorsque la mise en cache de niveau JVM est activée ou lorsque le générateur hilo est utilisé dans un environnement JTA.
|
hibernate.transaction.flush_before_completion |
Booléen. Si activé, la session sera automatiquement vidée avant la phase d'achèvement de la transaction. Une gestion de contexte de session automatique et intégrée est préférée.
|
hibernate.transaction.auto_close_session |
Booléen. Si activé, la session sera automatiquement fermée après la phase d'achèvement de la transaction. Une gestion de contexte de session automatique et intégrée est préférable.
|
12.2.4.5. Propriétés Hibernate diverses
Tableau 12.5. Propriétés
Nom de propriété | Description |
---|---|
hibernate.current_session_context_class |
Fournit une stratégie personnalisée pour l'étendue de la
Session « actuelle ». Les valeurs comprennent jta | thread | managed | custom.Class .
|
hibernate.query.factory_class |
Choisir l'implémentation de l'analyseur de HQL :
org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory ou org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory .
|
hibernate.query.substitutions |
Utilisé pour mapper à partir de jetons dans les requêtes Hibernate aux jetons SQL (les jetons peuvent avoir un nom fonctionnel ou littéral). Par exemple,
hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC .
|
hibernate.hbm2ddl.auto |
Valide ou exporte automatiquement le schéma DDL vers la base de données quand la
SessionFactory est créée. Avec create-drop , le schéma de base de données sera déposé lorsque la SessionFactory est fermée explicitement. Les options de valeur de propriété sont validate | update | create | create-drop
|
hibernate.hbm2ddl.import_files |
Les noms séparés par des virgules des fichiers optionnels contenant des déclarations SQL DML exécutées pendant la création de
SessionFactory . Cela est utile pour tester ou démontrer. Par exemple, en ajoutant des déclarations INSERT, la base de données peut être remplie avec un lot minimum de données quand elle est déployée. Exemple de valeur : /humans.sql,/dogs.sql .
L'ordre de fichier compte, puisque les déclarations d'un fichier donné sont exécutées avant la déclaration du fichier suivant. Ces déclarations ne sont exécutées que si le schéma est créé (c'est-à-dire si
hibernate.hbm2ddl.auto est défini comme create ou create-drop ).
|
hibernate.hbm2ddl.import_files_sql_extractor |
Le nom de classe d'un
ImportSqlCommandExtractor personnalisé. Valeur par défaut de SingleLineSqlCommandExtractor intégré. Cela est utile pour implémenter un analyseur dédié qui extrait une déclaration unique de SQL depuis chaque fichier d'importation. Hibernate fournit également MultipleLinesSqlCommandExtractor , qui prend en charge des instructions/commentaires et des chaînes citées réparties sur de multiples lignes (point-virgule obligatoire à la fin de chaque déclaration).
|
hibernate.bytecode.use_reflection_optimizer |
Booléen. Ceci est une propriété de niveau système, qui ne peut pas être définie dans le fichier
hibernate.cfg.xml . Elle permet l'utilisation de la manipulation bytecode au lieu de la réflexion d'exécution. La réflexion peut parfois être utile lors de la résolution de problèmes. Hibernate requiert toujours soit CGLIB ou javassist même si l'optimizer est éteint.
|
hibernate.bytecode.provider |
Javassist et cglib peuvent tous les deux être utilisés comme moteurs de manipulation de byte. La valeur par défaut est
javassist . La valeur de propriété est soit javassist soit cglib .
|
12.2.4.6. Dialectes SQL Hibernate
Important
hibernate.dialect
devrait être définie sur la sous-classe org.hibernate.dialect.Dialect
correcte pour la base de données de l'application. Si un dialecte est indiqué, Hibernate utilisera des valeurs par défaut raisonnables pour quelques unes des autres propriétés. Cela signifie qu'elles n'ont pas besoin d'être spécifiées manuellement.
Tableau 12.6. Dialectes SQL (hibernate.dialect
)
RDBMS | Dialecte |
---|---|
DB2 | org.hibernate.dialect.DB2Dialect |
DB2 AS/400 | org.hibernate.dialect.DB2400Dialect |
DB2 OS390 | org.hibernate.dialect.DB2390Dialect |
Firebird | org.hibernate.dialect.FirebirdDialect |
FrontBase | org.hibernate.dialect.FrontbaseDialect |
H2 Database | org.hibernate.dialect.H2Dialect |
HypersonicSQL | org.hibernate.dialect.HSQLDialect |
Informix | org.hibernate.dialect.InformixDialect |
Ingres | org.hibernate.dialect.IngresDialect |
Interbase | org.hibernate.dialect.InterbaseDialect |
Mckoi SQL | org.hibernate.dialect.MckoiDialect |
Microsoft SQL Server 2000 | org.hibernate.dialect.SQLServerDialect |
Microsoft SQL Server 2005 | org.hibernate.dialect.SQLServer2005Dialect |
Microsoft SQL Server 2008 | org.hibernate.dialect.SQLServer2008Dialect |
Microsoft SQL Server 2012 | org.hibernate.dialect.SQLServer2008Dialect |
MySQL5 | org.hibernate.dialect.MySQL5Dialect |
MySQL5 avec InnoDB | org.hibernate.dialect.MySQL5InnoDBDialect |
MySQL avec MyISAM | org.hibernate.dialect.MySQLMyISAMDialect |
Oracle (toute version) | org.hibernate.dialect.OracleDialect |
Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
Oracle 10g | org.hibernate.dialect.Oracle10gDialect |
Oracle 11g | org.hibernate.dialect.Oracle10gDialect |
Pointbase | org.hibernate.dialect.PointbaseDialect |
PostgreSQL | org.hibernate.dialect.PostgreSQLDialect |
PostgreSQL 9.2 | org.hibernate.dialect.PostgreSQL82Dialect |
Postgres Plus Advanced Server | org.hibernate.dialect.PostgresPlusDialect |
Progress | org.hibernate.dialect.ProgressDialect |
SAP DB | org.hibernate.dialect.SAPDBDialect |
Sybase | org.hibernate.dialect.SybaseASE15Dialect |
Sybase 15.7 | org.hibernate.dialect.SybaseASE157Dialect |
Sybase Anywhere | org.hibernate.dialect.SybaseAnywhereDialect |
12.2.5. Caches de second niveau
12.2.5.1. Les caches de second niveau
- Web Session Clustering
- Stateful Session Bean Clustering
- SSO Clustering
- Cache de second niveau d'Hibernate
12.2.5.2. Configurer un cache de second niveau pour Hibernate
Procédure 12.4. Créer et modifier le fichier hibernate.cfg.xml
Créer le fichier hibernate.cfg.xml
Créer le fichierhibernate.cfg.xml
dans le chemin de classe du déploiement. Pour plus de détails, voir Section 12.2.3.4, « Créer le fichier de configuration Hibernate dans Red Hat JBoss Developer Studio » .- Ajouter ces lignes d'XML au fichier
hibernate.cfg.xml
de votre application. Le XML doit être à l'intérieur des balises <session-factory>:<property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property>
- Ajouter un élément ci-dessous à la section <session-factory> de votre fichier
hibernate.cfg.xml
:Si le CacheManager Infinispan est lié au JNDI :
<property name="hibernate.cache.region.factory_class"> org.hibernate.cache.infinispan.JndiInfinispanRegionFactory </property> <property name="hibernate.cache.infinispan.cachemanager"> java:CacheManager </property>
Si le CacheManager Infinispan est autonome :
<property name="hibernate.cache.region.factory_class"> org.hibernate.cache.infinispan.InfinispanRegionFactory </property>
Infinispan est configuré au second niveau de cache dans Hibernate.
12.3. Annotations Hibernate
12.3.1. Annotations Hibernate
Tableau 12.7. Annotations définies d'Hibernate
Annotation | Description |
---|---|
AccessType | Type d'accès de propriété |
Tous | Définit une association ToOne pointant vers plusieurs types d'entités. L'association du type d'entité correspondant se fait à travers une colonne discriminatoire de métadonnées. Ce type de mappage devrait seulement être marginal. |
AnyMetaDef | Définit les métadonnées @Any et @ManyToAny. |
AnyMedaDefs | Définit les ensembles de métadonnées @Any et @ManyToAny. Peut être défini au niveau d'entité ou de paquetage. |
BatchSize | Taille de lot pour chargement SQL. |
Cache | Ajouter une stratégie de mise en cache à une entité root ou une collection. |
Cascade | Appliquer une stratégie de cascade sur une association |
Check | Contraintes de vérification SQL arbitraires qui peuvent être définies aux niveaux de classe, de propriété ou de collection. |
Columns | Prend en charge une gamme de colonnes. Utile pour les mappages de type utilisateur de composant. |
ColumnTransformer | Expression SQL personnalisée utilisée pour lire la valeur depuis et écrire une valeur vers une colonne. Utiliser pour le chargement/enregistrement d'objet direct ainsi que les requêtes. L'expression de lecture doit contenir exactement un "?" pour la valeur. |
ColumnTransformers | Annotation plurielle pour @ColumnTransformer. Utile lorsque plus d'une colonne utilise ce comportement. |
DiscriminatorFormula | Formule discriminatoire à placer à l'entité de root. |
DiscriminatorOptions | Annotation optionnelle pour exprimer les propriétés discriminatoires spécifiques à Hibernate. |
Entity | Prolonge l'entité avec les caractéristiques Hibernate. |
Fetch | Définit la stratégie de récupération utilisée pour l'association donnée. |
FetchProfile | Définit le profil de stratégie de récupération |
FetchProfiles | Annotation plurielle pour @FetchProfile. |
Filter | Ajoute des filtres à une entité ou une entité de cible d'une collection. |
FilterDef | Définition de Filtre. |
FilterDefs | Gamme de définitions de filtre. |
FilterJoinTable | Ajoute des filtres pour une collection rejoindre une table |
FilterJoinTables | Ajoute de multiples @FilterJoinTable à une collection |
Filters | Ajoute de multiples @Filters. |
Formula | A utiliser comme remplacement pour @Column dans la plupart des emplacements. La formule doit être un fragment SQL valide. |
Generated | Cette propriété annotée est générée par la base de données. |
GenericGenerator | Annotation de générateur décrivant toute sorte de générateur Hibernate d'une manière non-typée. |
GenericGenerators | Gamme de définitions de générateur génériques. |
Immutable |
Marque une Entité ou une Collection comme immuable. Si aucune annotation, l'élément est mutable.
Une entité immuable peut ne pas être mise à jour par l'application. Les mises à jour vers une entité immuable seront ignorées, mais aucune exception ne sera levée.
Placer @Immutable sur une collection rend la collection immuable, ce qui signifie que les additions et suppressions vers et depuis la collection ne sont pas autorisées. Une HibernateException est levée dans ce cas.
|
Index | Définit un indice de base de données. |
JoinFormula | A utiliser comme remplacement pour @JoinColumn dans la plupart des emplacements. La formule doit être un fragment SQL valable. |
LazyCollection | Définit le statut lazy d'une collection |
LazyToOne | Définit le statut lazy d'une association ToOne (c'est-à-dire OneToOne or ManyToOne). |
Loader | Ecrase la méthode FIND par défaut de Hibernate. |
ManyToAny | Définit une association ToMany en indiquant différent types d'entité. L'association du type d'entité correspondant se fait à travers une colonne discriminatoire de métadonnées. Ce type de mappage devrait seulement être marginal. |
MapKeyType | Définit le type de clé d'une mappe persistante. |
MetaValue | Représente une valeur discriminatoire associée à un type d'entité donné. |
NamedNativeQueries | Prolonge les NamedNativeQueries pour maintenir les objets NamedNativeQuery de Hibernate. |
NamedNativeQuery | Prolonge la NamedNativeQuery avec les caractéristiques Hibernate. |
NamedQueries | Etend les NamedQueries pour maintenir les objets NamedQuery de Hibernate. |
NamedQuery | Prolonge NamedQuery avec les caractéristiques Hibernate. |
IdNaturel | Indique que la propriété fait partie de l'identifiant naturel de l'entité. |
NotFound | Action à accomplir lorsqu'un élément est introuvable dans une association. |
OnDelete | Stratégie à utiliser pour la suppression de collections, de tableaux ou de sous-classes jointes. OnDelete des tableaux secondaires n'est pas actuellement pris en charge. |
OptimisticLock | Indique si une modification de la propriété annotée déclenchera une incrémentation de version d'entité. Si l'annotation n'est pas présente, la propriété est impliquée dans la stratégie de verrouillage optimiste (par défaut). |
OptimisticLocking | Utilisé pour définir le style de verrouillage optimiste à appliquer à une entité. Dans une hiérarchie, seulement valide sur l'entité de root. |
OrderBy | Ordonne une collection en utilisant l'ordre SQL (pas l'ordre HSL). |
ParamDef | Une définition de paramètre. |
Paramètre | Modèle clé/valeur |
Parent | Renvoie la référence de la propriété en tant que pointeur vers le propriétaire (en général l'entité propriétaire). |
Persister | Indique un persister personnalisé |
Polymorphisme | Utilisé pour définir le type de polymorphisme qu'Hibernate appliquera aux hiérarchies d'entité. |
Proxy | Configuration proxy et lazy d'une classe particulière. |
RowId | Prend en charge les caractéristiques de mappage ROWID de Hibernate |
Tri | Tri de collection (tri de niveau Java). |
Source | Annotation optionnelle conjointement avec les propriétés de Version et de version Timestamp. La valeur d'annotation décide de l'endroit où timestamp est généré. |
SQLDelete | Ecrase la méthode DELETE par défaut de Hibernate. |
SQLDeleteAll | Ecrase la méthode DELETE ALL par défaut de Hibernate. |
SQLInsert | Remplace la méthode INSERT INTO par défaut de Hibernate. |
SQLUpdate | Remplace la méthode UPDATE par défaut de Hibernate. |
Subselect | Mappe une entité immuable et en lecture seule vers une expression sous-sélection SQL donnée. |
Synchronize | S'assure qu'auto-flush fonctionne correctement et que les requêtes concernant l'entité dérivée ne renvoient pas des données obsolètes. Principalement utilisé avec Subselect. |
Table | Information complémentaire d'une table primaire ou secondaire |
Tables | Annotation plurielle de Table |
Cible | Définit une source explicite et évite la résolution de réflexion et de génériques. |
Tuplizer | Définit un tuplizer pour une entité ou un composant. |
Tuplizers | Définit un ensemble de tuplizers pour une entité ou un composant. |
Type | Type Hibernate. |
TypeDef | Définition d'un type Hibernate |
TypeDefs | Gamme de définition de Type Hibernate |
Where | Clause Where à ajouter à l'élément Entité ou entité de cible d'une collection. La clause est écrite en SQL. |
WhereJoinTable | Clause Where à ajouter à la collection joindre une table. La clause est écrite en SQL. |
Note
12.4. Langage de recherche Hibernate
12.4.1. Langage de recherche d'Hibernate
12.4.2. Déclarations HQL
SELECT
, UPDATE
, DELETE
, et INSERT
. La déclaration INSERT
de HQL n'a pas d'équivalent dans JPQL.
Important
UPDATE
ou DELETE
est exécutée.
Tableau 12.8. Déclarations HQL
Déclaration | Description |
---|---|
SELECT |
Le BNF pour les déclarations
SELECT dans HQL est :
select_statement :: = [select_clause] from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]
La déclaration
SELECT de HQL la plus simple qui soit est sous la forme :
de com.acme.Cat |
UDPATE | Le BNF pour la déclaration UPDATE dans HQL est le même que dans JPQL. |
DELETE | Le BNF pour les déclarations DELETE dans HQL est le même que dans JPQL. |
12.4.3. La déclaration INSERT
INSERT
. Il n'existe pas d'équivalent JPQL. Le BNF d'une déclaration INSERT
HQL est :
insert_statement ::= insert_clause select_statement insert_clause ::= INSERT INTO entity_name (attribute_list) attribute_list ::= state_field[, state_field ]*
attribute_list
est analogue à la column specification
dans la déclaration INSERT
de SQL. Pour les entités impliquées dans un héritage mappé, seuls les attributs définis directement sur l'entité nommée peuvent être utilisés dans la attribute_list
. Les propriétés de sur-classe ne sont pas autorisées et les propriétés de sous-classe sont inutiles. En d'autres termes, les déclarations INSERT
sont, par nature, non-polymorphes.
Avertissement
select_statement
peut être n'importe quelle requête sélectionnée HQL valide, avec l'avertissement que les types de renvoi doivent correspondre aux types attendus par l'insertion. Actuellement, cela est vérifié lors de la compilation de requêtes plutôt que de permettre une vérification reléguée à la base de données. Cela pourrait causer des problèmes entre les types Hibernate qui sont equivalent et non pas equal. Par exemple, cela pourrait entraîner des problèmes avec des incompatibilités entre un attribut mappé en tant que org.hibernate.type.DateType
et un attribut défini en tant que org.hibernate.type.TimestampType
, bien que la base de données puisse ne pas faire la distinction ou puisse être capable de traiter la conversion.
attribute_list
, dans quel cas sa valeur sera prise à partir de l'expression sélectionnée correspondante, ou l'omettre à partir de la attribute_list
, dans quel cas une valeur générée sera utilisée. Cette dernière option n'est disponible que lors de l'utilisation de générateurs d'identifiants qui opèrent « dans la base de données ». Tenter d'utiliser cette option avec n'importe quel générateur de type « en mémoire » causera une exception en cours de traitement.
attribute_list
, dans quel cas sa valeur sera prise à partir des expressions sélectionnées correspondantes, ou l'omettre à partir de la attribute_list
dans quel cas la seed value
définie par le org.hibernate.type.VersionType
correspondant sera utilisé.
Exemple 12.3. Exemple d'énoncé de requêtes INSERT
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ..."; int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
12.4.4. La clause FROM
FROM
est chargée de définir l'étendue des types de modèles objets disponibles au reste de la requête. Elle est également chargée de définir toutes les « variables d'identification » disponibles au reste de la requête.
12.4.5. La clause WITH
WITH
pour qualifier les conditions de jointure. Cela est spécifique à HQL. JPQL ne définit pas cette fonctionnalité.
Exemple 12.4. Exemple de jointure de with-clause
select distinct c from Customer c left join c.orders o with o.value > 5000.00
with clause
font partie de on clause
dans le SQL généré contrairement aux autres requêtes dans cette section où les conditions HQL/JPQL font partie de la where clause
dans le SQL généré. La différence dans cet exemple en particulier n'est sûrement pas très importante. La with clause
est quelques fois nécessaire pour des requêtes plus compliquées.
12.4.6. Mises à jour, insertions et suppressions en bloc
Avertissement
( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?
.
Note
FROM
et la clause WHERE
sont optionnels.
Exemple 12.5. Exemple de déclaration de mise à jour en bloc
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlUpdate = "update Company set name = :newName where name = :oldName"; int updatedEntities = s.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
Exemple 12.6. Exemple de déclaration de suppression en bloc
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlDelete = "delete Company where name = :oldName"; int deletedEntities = s.createQuery( hqlDelete ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
int
renvoyée par la méthode Query.executeUpdate()
indique le nombre d'entités qui se trouvent dans la base de données et qui sont affectées par l'opération.
Company
(entreprises) qui contiennent oldName
dans le tableau, mais aussi dans les tableaux de liaisons. Donc, un tableau d'entreprises (Companies) qui se trouve dans une relation BiDirectionelle ManyToMany avec un tableau d'employés (Employee) perdrait des lignes dans le tableau de liaison correspondant Company_Employee
suite à l'exécution de l'exemple précédent.
int deletedEntries
ci-dessus contiendra le nombre de lignes affectées par cette opération, y compris les lignes qui se trouvent dans les tableaux de liaisons.
INSERT INTO EntityName properties_list select_statement
.
Note
Exemple 12.7. Exemple de déclaration d'insertion en bloc
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlInsert = "insert into Account (id, name) select c.id, c.name from Customer c where ..."; int createdEntities = s.createQuery( hqlInsert ) .executeUpdate(); tx.commit(); session.close();
id
via SELECT, un ID sera généré, tant que la base de données sous-jacente prend en charge les clés générées automatiquement. La valeur de retour de cette opération d'insertion en bloc est le nombre d'entrées créées dans la base de données.
12.4.7. Références de collection
Exemple 12.8. Exemple de références de collection
select c from Customer c join c.orders o join o.lineItems l join l.product p where o.status = 'pending' and p.status = 'backorder' // alternate syntax select c from Customer c, in(c.orders) o, in(o.lineItems) l join l.product p where o.status = 'pending' and p.status = 'backorder'
o
fait référence au type de modèle de variable Order
qui correspond au type d'éléments de l'association Customer#orders
.
IN
. Les deux formes sont équivalentes. La forme choisie par une application est tout simplement une question de goût.
12.4.8. Expressions de chemins qualifiés
Tableau 12.9. Expressions de chemins qualifiés
Expression | Description |
---|---|
VALUE |
Correspond à la valeur de la collection. C'est équivalent à ne pas spécifier de qualificateur. Utile pour démontrer une intention. Valide pour n'importe quel type de référence collection-valued.
|
INDEX |
Selon les règles HQL, ceci est valable pour les mappages et pour les listes qui spécifient une annotation
javax.persistence.OrderColumn pour désigner la clé de Mappage ou la position de la Liste (aka la valeur OrderColumn). JPQL toutefois réserve ceci pour une utilisation dans la case Liste et ajoute la clé dans la case Mappage. Les applications intéressées par la transférabilité des fournisseurs JPA doivent tenir compte de cette distinction.
|
KEY |
Uniquement valide pour le mappage. Se réfère à la clé de mappage. Si la clé elle-même est une entité, elle peut naviguer davantage.
|
ENTRY |
Uniquement valide pour les mappages. Voir le tuple (combinaison d'une clé et de sa valeur) logique du Mappagev
java.util.Map.Entry . ENTRY n'est valide qu'en tant que chemin final et uniquement pour la clause sélectionnée.
|
Exemple 12.9. Exemple de références de collection qualifiée
// Product.images is a Map<String,String> : key = a name, value = file path // select all the image file paths (the map value) for Product#123 select i from Product p join p.images i where p.id = 123 // same as above select value(i) from Product p join p.images i where p.id = 123 // select all the image names (the map key) for Product#123 select key(i) from Product p join p.images i where p.id = 123 // select all the image names and file paths (the 'Map.Entry') for Product#123 select entry(i) from Product p join p.images i where p.id = 123 // total the value of the initial line items for all orders for a customer select sum( li.amount ) from Customer c join c.orders o join o.lineItems li where c.id = 123 and index(li) = 1
12.4.9. Fonctions scalaires
12.4.10. Fonctions standardisées HQL
Tableau 12.10. Fonctions standardisées HQL
Fonction | Description |
---|---|
BIT_LENGTH |
Renvoie la longueur des données binaires.
|
CAST |
Effectue une transcription SQL. La cible de transcription doit désigner le type de mappage Hibernate à utiliser. Voir le chapitre sur les types de données pour obtenir plus d'informations.
|
EXTRACT |
Effectue une extraction SQL sur les valeurs Heure/Date. L'extraction soutire une partie de la valeur (l'année, par exemple). Voir les formes d'abréviation ci-dessous.
|
SECOND |
Abréviation pour extraire une Seconde.
|
MINUTE |
Forme d'abréviation pour extraire une Minute.
|
HOUR |
Abréviation pour extraire une Heure.
|
DAY |
Abréviation pour extraire un Jour.
|
MONTH |
Abréviation pour extraire un Mois.
|
YEAR |
Abréviation pour extraire une Année.
|
STR |
Abréviation pour la conversion d'une valeur en caractère.
|
addSqlFunction
de org.hibernate.cfg.Configuration
12.4.11. Opération de concaténation
CONCAT
). N'est pas défini par JPQL, donc les applications portables doivent éviter de l'utiliser. L'opérateur de concaténation vient d'un opérateur de concaténation SQL - ||
.
Exemple 12.10. Exemple d'opération de concaténation
select 'Mr. ' || c.name.first || ' ' || c.name.last from Customer c where c.gender = Gender.MALE
12.4.12. Instanciation dynamique
Exemple 12.11. Exemple d'instanciation dynamique - Constructor
select new Family( mother, mate, offspr ) from DomesticCat as mother join mother.mate as mate left join mother.kittens as offspr
Exemple 12.12. Exemple d'instanciation dynamique - List
select new list(mother, offspr, mate.name) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
Exemple 12.13. Exemple d'instanciation dynamique - Mappe
select new map( mother as mother, offspr as offspr, mate as mate ) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr select new map( max(c.bodyWeight) as max, min(c.bodyWeight) as min, count(*) as n ) from Cat cxt
12.4.13. Prédicats HQL
TRUE
ou FALSE
, bien que les comparaison booléènnes comportant des NULL résolvent les UNKNOWN
.
Prédicats HQL
- Prédicat Nullness
- Cherchez une valeur nulle et sans effet. Peut s'appliquer à des références d'attributs de base, à des références d'entité et à des paramètres. De plus, HQL permet une application à des types de composants/intégrables.
Exemple 12.14. Exemples de vérification de Nullness
// select everyone with an associated address select p from Person p where p.address is not null // select everyone without an associated address select p from Person p where p.address is null
- Prédicat Like
- Procède à une comparaison d'équivalence entre des valeurs de chaîne. La syntaxe est la suivante :
like_expression ::= string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
Les sémantiques suivent l'expression Like SQL. Lapattern_value
est un modèle qui tente de correspondre à l'expression de chaînestring_expression
. Tout comme pour l'expresion «like» SQL, le modèle oupattern_value
peut utiliser "_" et "%" comme caractères génériques. Les significations sont les mêmes. "_" correspond à n'importe quel caractère unique. "%" correspond à un certain nombre de caractères.Le caractèreescape
optionnel est utilisé pour spécifier un caractère d'échappement en combinaison à ce qui est impliqué par "_" ou "%" dans le modèle oupattern_value
. Ceci est utile quand on doit chercher des modèles contenant des "_" ou des "%".Exemple 12.15. Exemples de prédicats «Like»
select p from Person p where p.name like '%Schmidt' select p from Person p where p.name not like 'Jingleheimmer%' // find any with name starting with "sp_" select sp from StoredProcedureMetadata sp where sp.name like 'sp|_%' escape '|'
- Prédicat Between
- Ressemble à l'expression SQL
BETWEEN
. Détermine qu'une valeur est entre 2 autres valeurs. Tous les opérandes doivent avoir des types comparables.Exemple 12.16. Exemples de prédicats «Between»
select p from Customer c join c.paymentHistory p where c.id = 123 and index(p) between 0 and 9 select c from Customer c where c.president.dateOfBirth between {d '1945-01-01'} and {d '1965-01-01'} select o from Order o where o.total between 500 and 5000 select p from Person p where p.name between 'A' and 'E'
12.4.14. Comparaisons relationnelles
Exemple 12.17. Exemples de comparaisons relationnelles
// numeric comparison select c from Customer c where c.chiefExecutive.age < 30 // string comparison select c from Customer c where c.name = 'Acme' // datetime comparison select c from Customer c where c.inceptionDate < {d '2000-01-01'} // enum comparison select c from Customer c where c.chiefExecutive.gender = com.acme.Gender.MALE // boolean comparison select c from Customer c where c.sendEmail = true // entity type comparison select p from Payment p where type(p) = WireTransferPayment // entity value comparison select c from Customer c where c.chiefExecutive = c.chiefTechnologist
ALL
, ANY
, SOME
. SOME
et ANY
sont synonymes.
ALL
passe à true si la comparaison est true pour toutes les valeurs qui se trouvent dans le résultat de la sous-requête. Passe à false si le résultat de la sous-requête est vide.
Exemple 12.18. Exemple de qualificateur de comparaison de sous-requête ALL
// select all players that scored at least 3 points // in every game. select p from Player p where 3 > all ( select spg.points from StatsPerGame spg where spg.player = p )
ANY
/SOME
passe à true si la comparaison est true pour toutes les valeurs qui se trouvent dans le résultat de la sous-requête. Passe à false si le résultat de la sous-requête est vide.
12.4.15. Prédicat IN
IN
effectue une vérification pour savoir si une valeur donnée fait partie de la liste des valeurs. Sa syntaxe est la suivante :
in_expression ::= single_valued_expression [NOT] IN single_valued_list single_valued_list ::= constructor_expression | (subquery) | collection_valued_input_parameter constructor_expression ::= (expression[, expression]*)
single_valued_expression
et des valeurs individuelles qui se trouvent dans la liste single_valued_list
doivent être consistantes. JPQL limite les types valides ici à des chaînes, des valeurs numériques, date, heure, heure/date, et enum. Dans JPQL, single_valued_expression
ne peut se référer qu'à des :
- "state fields", son terme pour attributs simples. Cela exclut en particulier les attributs composants/intégrés et associations.
- expression de type d'entité.
single_valued_expression
peut désigner un bien plus large éventail de types d'expression. Les association de valeurs simples sont autorisées, ainsi que les attributs composant/intégré, bien que cette fonctionnalité dépend du niveau de soutien pour le tuple ou pour la «syntaxe de constructeur de valeur de ligne» dans la base de données sous-jacente. En outre, les HQL ne limitent le type de valeur en aucune façon, bien que les développeurs d'applications doivent être conscients que les différents types peuvent encourir un soutien limité basé sur le fournisseur de base de données sous-jacent. C'est en grande partie la raison des limitations JPQL
constructor_expression
et collection_valued_input_parameter
, la liste des valeurs ne doit pas rester vide; elle doit contenir une valeur au moins.
Exemple 12.19. Exemples de prédicat
select p from Payment p where type(p) in (CreditCardPayment, WireTransferPayment) select c from Customer c where c.hqAddress.state in ('TX', 'OK', 'LA', 'NM') select c from Customer c where c.hqAddress.state in ? select c from Customer c where c.hqAddress.state in ( select dm.state from DeliveryMetadata dm where dm.salesTax is not null ) // Not JPQL compliant! select c from Customer c where c.name in ( ('John','Doe'), ('Jane','Doe') ) // Not JPQL compliant! select c from Customer c where c.chiefExecutive in ( select p from Person p where ... )
12.4.16. SQL Ordering
ORDER BY
est utilisée pour spécifier les valeurs sélectionnées à utiliser pour ordonnancer le résultat. Les types d'expressions considérées comme valides pour la clause order-by sont les suivantes :
- champs d'état
- attributs composant/intégré
- expressions scalaires comme les opérations arithmétiques, les fonctions, etc.
- variable d'identification déclarée dans la clause Select pour n'importe quel type d'expression antérieure.
ASC
(ascendant) ou DESC
(descendant) pour indiquer la direction d'ordonnancement désirée.
Exemple 12.20. Exemple Order-by
// legal because p.name is implicitly part of p select p from Person p order by p.name select c.id, sum( o.total ) as t from Order o inner join o.customer c group by c.id order by t
12.5. Services Hibernate
12.5.1. Les services Hibernate
12.5.2. Contrats de service
org.hibernate.service.Service
. Hibernate l'utilise en interne pour des raisons de sécurité de base.
org.hibernate.service.spi.Startable
et org.hibernate.service.spi.Stoppable
pour recevoir des notifications de démarrage ou d'arrêt. Il existe un autre service org.hibernate.service.spi.Manageable
qui indique que le service est gérable dans le JMX dans la mesure où l'intégration JMX est activée.
12.5.3. Types de dépendances de service
- @
org.hibernate.service.spi.InjectService
- Toute méthode de classe d'implémentation de service qui accepte un seul paramètre et qui est annotée par @
InjectService
est considérée comme réclamant une injection de la part d'un autre service.Par défaut, le type du paramètre de méthode doit correspondre au rôle de service à injecter. Si le type de paramètre est différent du rôle de service, l'attributserviceRole
duInjectService
devra être utilisé pour nommer explicitement le rôle.Les services injectés sont requis par défaut, c'est à dire que le démarrage échouera si un service dépendant nommé est manquant. Si le service injecté est optionnel, l'attributrequis
d'InjectService
devra être déclaréfalse
(la valeur par défaut esttrue
). org.hibernate.service.spi.ServiceRegistryAwareService
- La seconde approche est une approche pull pour laquelle le service implémente l'interface de service optionnelle
org.hibernate.service.spi.ServiceRegistryAwareService
qui déclare une méthodeinjectServices
unique.En cours de démarrage, Hibernate injectera leorg.hibernate.service.ServiceRegistry
lui-même dans les services qui implémentent cette interface. Le service pourra alors utiliser la référenceServiceRegistry
pour trouver les services additionnels dont il pourrait avoir besoin.
12.5.4. Le ServiceRegistry
12.5.4.1. Le ServiceRegistry
org.hibernate.service.ServiceRegistry
. Le but principal d'un registre de service est de contenir, de gérer et de donner accès aux services.
org.hibernate.service.ServiceRegistryBuilder
pour créer une instance org.hibernate.service.ServiceRegistry
.
Exemple 12.21. Utiliser ServiceRegistryBuilder pour créer un ServiceRegistry
ServiceRegistryBuilder registryBuilder = new ServiceRegistryBuilder( bootstrapServiceRegistry ); ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();
12.5.5. Services personnalisés
12.5.5.1. Services personnalisés
org.hibernate.service.ServiceRegistry
est créé, il est considéré comme immuable. Les services eux-mêmes pourraient accepter une reconfiguration, mais ici, l'immuabilité signifie un ajout/remplacement des services. Et donc, un autre rôle assuré par org.hibernate.service.ServiceRegistryBuilder
est de permettre de peaufiner des services qui figureront dans le org.hibernate.service.ServiceRegistry
qu'il génère.
org.hibernate.service.ServiceRegistryBuilder
à propos de services personnalisés.
- Implémenter une classe
org.hibernate.service.spi.BasicServiceInitiator
pour contrôler la construction sur demande de la classe du service et y ajouter leorg.hibernate.service.ServiceRegistryBuilder
via sa méthodeaddInitiator
. - Il vous suffit d'instancier la classe de service et de l'ajouter au
org.hibernate.service.ServiceRegistryBuilder
via sa méthodeaddService
.
Exemple 12.22. Utiliser ServiceRegistryBuilder pour remplacer un service existant par un service personnalisé
ServiceRegistryBuilder registryBuilder = new ServiceRegistryBuilder( bootstrapServiceRegistry ); registryBuilder.addService( JdbcServices.class, new FakeJdbcService() ); ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry(); public class FakeJdbcService implements JdbcServices{ @Override public ConnectionProvider getConnectionProvider() { return null; } @Override public Dialect getDialect() { return null; } @Override public SqlStatementLogger getSqlStatementLogger() { return null; } @Override public SqlExceptionHelper getSqlExceptionHelper() { return null; } @Override public ExtractedDatabaseMetaData getExtractedMetaDataSupport() { return null; } @Override public LobCreator getLobCreator(LobCreationContext lobCreationContext) { return null; } @Override public ResultSetWrapper getResultSetWrapper() { return null; } }
12.5.6. Le registre Bootstrap
12.5.6.1. Le registre Boot-strap
ClassLoaderService
qui en est un parfait exemple. Même la résolution des fichiers de configuration doit avoir accès aux services de chargement de classes (pour la recherche de ressources). C'est le registre racine (sans parent) en conditions normales d'utilisation.
org.hibernate.service.BootstrapServiceRegistryBuilder
.
12.5.6.2. Utilisation de BootstrapServiceRegistryBuilder
Exemple 12.23. Utilisation de BootstrapServiceRegistryBuilder
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder() // pass in org.hibernate.integrator.spi.Integrator instances which are not // auto-discovered (for whatever reason) but which should be included .with( anExplicitIntegrator ) // pass in a class-loader Hibernate should use to load application classes .with( anExplicitClassLoaderForApplicationClasses ) // pass in a class-loader Hibernate should use to load resources .with( anExplicitClassLoaderForResources ) // see BootstrapServiceRegistryBuilder for rest of available methods ... // finally, build the bootstrap registry with all the above options .build();
12.5.6.3. Les services BootstrapRegistry
org.hibernate.service.classloading.spi.ClassLoaderService
- la capacité de localiser des classes d'application
- la capacité de localiser des classes d'intégration
- la capacité de localiser des ressources (fichiers de propriétés, fichiers xml, etc.)
- la capacité à charger
java.util.ServiceLoader
Note
org.hibernate.integrator.spi.IntegratorService
java.util.ServiceLoader
fournie par org.hibernate.service.classloading.spi.ClassLoaderService
pour découvrir les implémentations du contrat org.hibernate.integrator.spi.Integrator
.
/META-INF/services/org.hibernate.integrator.spi.Integrator
et le rendraient disponible sur le chemin de classe.
java.util.ServiceLoader
. Ce dernier répertorie, sur chaque ligne, le nom complet des classes qui mettent en œuvre l'interface org.hibernate.integrator.spi.Integrator
.
12.5.7. Le registre SessionFactory
12.5.7.1. Le registre SessionFactory
org.hibernate.SessionFactory
donnée, les instances de service de ce groupe appartiennent explicitement à une org.hibernate.SessionFactory
unique.
org.hibernate.SessionFactory
pour pouvoir être initiées. Ce registre spécial est org.hibernate.service.spi.SessionFactoryServiceRegistry
12.5.7.2. Services SessionFactory
org.hibernate.event.service.spi.EventListenerRegistry
- Description
- Service pour gérer les listeners d'événements
- Initiateur
org.hibernate.event.service.internal.EventListenerServiceInitiator
- Implémentations
org.hibernate.event.service.internal.EventListenerRegistryImpl
12.5.8. Intégrateurs
12.5.8.1. Intégrateurs
org.hibernate.integrator.spi.Integrator
a pour but de procurer un simple moyen de permettre aux développeurs de raccrocher un processus de création de SessionFactory en état de fonctionnement. L'interface org.hibernate.integrator.spi.Integrator
définit 2 méthodes intéressantes : integrate
permet de se joindre à un processus de création; disintegrate
nous permet de raccrocher la fermeture de la SessionFactory.
Note
org.hibernate.integrator.spi.Integrator
, une forme surchargée de integrate
qui accepte un org.hibernate.metamodel.source.MetadataImplementor
à la place d'une org.hibernate.cfg.Configuration
. Cette forme a pour dessein d'être utilisée avec le nouveau code de méta modèle qui devra est opérationnel dans 5.0.
12.5.8.2. Cas d'utilisation d'Integrator
org.hibernate.integrator.spi.Integrator
actuellement consistent à enregistrer des listeners d'événements et de fournir des services (voir org.hibernate.integrator.spi.ServiceContributingIntegrator
). Dans 5.0, nous espérons étendre ceci afin de permettre de modifier les méta modèles qui décrivent le mappage entre les modèles relationnels et les objets.
Exemple 12.24. Enregistrement des listeners d'événements
public class MyIntegrator implements org.hibernate.integrator.spi.Integrator { public void integrate( Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { // As you might expect, an EventListenerRegistry is the thing with which event listeners are registered It is a // service so we look it up using the service registry final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); // If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an // implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this eventListenerRegistry.addDuplicationStrategy( myDuplicationStrategy ); // EventListenerRegistry defines 3 ways to register listeners: // 1) This form overrides any existing registrations with eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, myCompleteSetOfListeners ); // 2) This form adds the specified listener(s) to the beginning of the listener chain eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst ); // 3) This form adds the specified listener(s) to the end of the listener chain eventListenerRegistry.appendListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast ); } }
12.6. Validation d'un bean
12.6.1. Bean Validation
bean-validation
: Section 1.4.1.1, « Accès aux Quickstarts ».
12.6.2. Hibernate Validator
12.6.3. Contraintes de validation
12.6.3.1. Les contraintes de validation
12.6.3.2. Créer une annotation de contrainte dans Red Hat JBoss Developer Studio
Cette tâche couvre le processus de création d'une annotation de contrainte dans Red Hat JBoss Developer Studio, à utiliser dans une application Java.
Conditions préalables
Procédure 12.5. Créer une annotation de contrainte
- Ouvrir un projet Java dans Red Hat JBoss Developer Studio
Créer un ensemble de données
Une annotation de contrainte requiert un ensemble de données qui définissent les valeurs qui conviennent.- Cliquer à droite sur le dossier racine du projet dans la section Project Explorer.
- Sélectionner Nouveau → Enum.
- Configurer les éléments suivants :
- Package :
- Nom :
- Cliquer sur le bouton Ajouter... pour ajouter des interfaces selon les besoins.
- Cliquer sur Terminer pour créer le fichier.
- Ajouter un ensemble de valeurs à l'ensemble des données et cliquer sur Sauvegarde.
Exemple 12.25. Exemple d'ensemble de données
package com.example; public enum CaseMode { UPPER, LOWER; }
Créer le fichier Annotation
Créer une nouvelle classe Java- Configurer l'annotation de la contrainte et cliquer sur Sauvegarde.
Exemple 12.26. Exemple de fichier d'annotation de contrainte
package com.mycompany; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Target( { METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = CheckCaseValidator.class) @Documented public @interface CheckCase { String message() default "{com.mycompany.constraints.checkcase}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; CaseMode value(); }
- Résultat
- Une annotation de contrainte personnalisée possédant un ensemble de valeurs possibles a été créée, prête à l'utilisation dans le projet Java.
12.6.3.3. Contraintes d'Hibernate Validator
Tableau 12.11. Contraintes intégrées
Annotation | + | Vérification du runtime | Impact des métadonnées Hibernate |
---|---|---|---|
@Length(min=, max=) | propriété (String) | Vérifier si la longueur de la chaîne de caractères correspond à la plage. | La colonne sera définie à la longueur maximum. |
@Max(value=) | propriété (numérique ou représentation par chaîne d'un numérique) | Vérifiez si la valeur est inférieure ou égale à la valeur maximum. | Ajouter un contrôle sur la colonne. |
@Min(value=) | propriété (numérique ou représentation par chaîne d'un numérique) | Vérifier si la valeur est supérieure ou égale à la valeur minimale. | Ajouter un contrôle sur la colonne. |
@NotNull | propriété | Vérifier si la valeur est non nulle. | Colonne(s) non nulles. |
@NotEmpty | propriété | Vérifier si la chaîne est non nulle ou vide. Vérifier si la connexion est non nulle ou vide. | Colonne(s) non nulles (pour Chaîne). |
@Past | propriété (date ou calendrier) | Vérifier si la date est dans le passé. | Ajouter un contrôle sur la colonne. |
@Future | propriété (date ou calendrier) | Vérifier si la date est dans le futur. | Aucun(e). |
@Pattern(regex="regexp", flag=) or @Patterns( {@Pattern(...)} ) | propriété (String) | Vérifier si la propriété correspond à une expression standard avec un indicateur de correspondance (voir java.util.regex.Pattern ). | Aucun(e). |
@Range(min=, max=) | propriété (numérique ou représentation par chaîne d'un numérique) | Vérifier si la valeur est comprise entre la valeur minimum et la valeur maximum (comprise). | Ajouter un contrôle sur la colonne. |
@Size(min=, max=) | propriété (tableau, collection, mappe) | Vérifier si la taille de l'élément est comprise entre la valeur minimum et la valeur maximum (comprise). | Aucun(e). |
@AssertFalse | propriété | Vérifier si la méthode évalue à false (utile pour les contraintes en code et non pas sous forme d'annotations). | Aucun(e). |
@AssertTrue | propriété | Vérifier si la méthode évalue à true (utile pour les contraintes en code et non pas sous forme d'annotations). | Aucun(e). |
@Valid | propriété (objet) | Procède à une validation récursive de l'objet associé. Si l'objet correspond à une collection ou à un tableau, les éléments seront validés de façon récursive. Si l'objet est une mappe, les éléments correspondant à la valeur seront validés de façon récursive. | Aucun(e). |
propriété (String) | Vérifier si la chaîne de caractères est bien conforme à la spécification de l'adresse email. | Aucun(e). | |
@CreditCardNumber | propriété (String) | Vérifier si la chaîne de caractères correspond à un numéro de carte de crédit formaté (dérivatif de l'algorithme Luhn). | Aucun(e). |
@Digits(integerDigits=1) | propriété (numérique ou représentation par chaîne d'un numérique) | Vérifier si la propriété est un numéro qui comprend un maximum de integerDigits chiffres et fractionalDigits chiffres sous forme de fraction. | Définit la précision et l'échelle de la colonne. |
@EAN | propriété (String) | Vérifie si la chaîne correspond à un code UPC-A ou EAN formaté comme il faut. | Aucun(e). |
12.6.4. Configuration
12.6.4.1. Exemple de fichier de configuration de validation
Exemple 12.27. validation.xml
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration"> <default-provider> org.hibernate.validator.HibernateValidator </default-provider> <message-interpolator> org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator </message-interpolator> <constraint-validator-factory> org.hibernate.validator.engine.ConstraintValidatorFactoryImpl </constraint-validator-factory> <constraint-mapping> /constraints-example.xml </constraint-mapping> <property name="prop1">value1</property> <property name="prop2">value2</property> </validation-config>
12.7. Envers
12.7.1. Hibernate Envers
@Audited
, et ils stockent l'historique des modifications apportées à l'entité. Les données peuvent ensuite être récupérées et interrogées.
- vérifier tous les mappages définis par la spécification JPA,
- vérifier tous les mappages qui étendent la spécification JPA,
- vérifier toules les entités mappées ou qui utilisent l'API Hibernate natif
- journaliser des données pour chaque révision par l'intermédiaire d'une entité de révision, et
- interroger les données historiques.
12.7.2. Audit de classes persistantes
@Audited
. Quand l'annotation s'applique à une classe, un tableau est créé, et ce tableau stocke l'historique de révision de l'entité.
12.7.3. Stratégies d'auditing
12.7.3.1. Stratégies d'auditing
- Stratégie d'audit par défaut
- Cette stratégie persiste les données d'audit avec la révision de départ. Pour chaque ligne insérée, mise à jour ou effacée dans un tableau audité, une ou plusieurs lignes seront insérées dans les tableaux d'audit, avec la révision de départ de sa validité.Les lignes des tableaux d'audit ne sont jamais mises à jour suite à une insertion. Les demandes d'informations d'audit utilisent des sous-requêtes pour sélectionner les lignes qui s'appliquent aux tableaux d'audit, et qui sont lentes et difficiles à indexer.
- Stratégie d'audit de validité
- Cette stratégie stocke la révision de départ, ainsi que la révision de fin de l'information d'audit. Pour chaque rangée insérée, mise à jour ou effacée dans un tableau audité, une ou plusieurs rangées seront insérées dans les tableaux d'audit, avec la révision de départ de sa validité.En même temps, le champ de révision de fin des lignes d'audit précédentes (si disponible) est défini pour cette révision. Les demandes d'informations d'audit peuvent ensuite utiliser entre révision de départ ou de fin, à la place des sous-requêtes. Cela signifie que persister les informations d'audit est un peu plus lent à cause des mises à jour supplémentaires, mais la récupération d'informations d'audit est beaucoup plus rapide.Cela peut également être amélioré en ajoutant des indices supplémentaires.
12.7.3.2. Définir la stratégie d'auditing
Il y a deux stratégies d'auditing prises en charge par JBoss EAP 6 : la stratégie d'audit de validité ou la stratégie d'audit par défaut. Cette tâche couvre les étapes nécessaires requises pour définir la stratégie d'auditing d'une application.
Procédure 12.6. Définir une stratégie d'auditing
- Configurer la propriété
org.hibernate.envers.audit_strategy
qui se trouve dans le ficherpersistence.xml
de l'application. Si la propriété est définie dans le fichierpersistence.xml
, alors la stratégie d'audit par défaut sera utilisée.Exemple 12.28. Définir la stratégie d'auditing par défaut
<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.DefaultAuditStrategy"/>
Exemple 12.29. Définir la stratégie d'auditing de validité
<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.ValidityAuditStrategy"/>
12.7.4. Introduction à l'auditing d'entités
12.7.4.1. Ajouter un support d'auditing à une entité JPA
Procédure 12.7. Ajouter un support d'auditing à une entité JPA
- Configurer les paramètres d'auditing disponibles qui conviennent au déploiement : Section 12.7.5.1, « Configurer les paramètres Envers ».
- Ouvrir l'entité JPA à auditer.
- Importer l'interface
org.hibernate.envers.Audited
. - Appliquer l'annotation
@Audited
à chaque champ ou propriété à auditer, ou l'appliquer une seule fois à toute la classe.Exemple 12.30. Audit de deux champs
import org.hibernate.envers.Audited; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.Column; @Entity public class Person { @Id @GeneratedValue private int id; @Audited private String name; private String surname; @ManyToOne @Audited private Address address; // add getters, setters, constructors, equals and hashCode here }
Exemple 12.31. Audit d'une classe complète
import org.hibernate.envers.Audited; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.Column; @Entity @Audited public class Person { @Id @GeneratedValue private int id; private String name; private String surname; @ManyToOne private Address address; // add getters, setters, constructors, equals and hashCode here }
L'entité JPA a été configurée pour l'auditing. Un tableau intitulé Entity_AUD
sera créé pour stocker les changements historiques.
12.7.5. Configuration
12.7.5.1. Configurer les paramètres Envers
Procédure 12.8. Configurer les paramètres Envers
- Ouvrir le fichier
persistence.xml
de l'application. - Ajouter, supprimer ou configurer les propriétés Envers selon les besoins. Pour obtenir une liste complète des propriétés disponibles, consulter Section 12.7.5.4, « Propriétés de configuration Envers ».
Exemple 12.32. Exemples de paramètres Envers
<persistence-unit name="mypc"> <description>Persistence Unit.</description> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.cache.use_second_level_cache" value="true" /> <property name="hibernate.cache.use_query_cache" value="true" /> <property name="hibernate.generate_statistics" value="true" /> <property name="org.hibernate.envers.versionsTableSuffix" value="_V" /> <property name="org.hibernate.envers.revisionFieldName" value="ver_rev" /> </properties> </persistence-unit>
- Résultat
- L'auditing a été configuré pour toutes les entités JPA de l'application.
12.7.5.2. Activer ou désactiver l'auditing en cours d'exécution
Cette tâche couvre les étapes de configuration requises pour activer/désactiver l'auditing de version d'entité en cours d'exécution.
Procédure 12.9. Activer/désactiver l'auditing
- Sous-classe la classe
AuditEventListener
. - Remplace les méthodes suivantes appelées sur les événements Hibernate :
- onPostInsert
- onPostUpdate
- onPostDelete
- onPreUpdateCollection
- onPreRemoveCollection
- onPostRecreateCollection
- Spécifie la sous-classe comme listener des événements.
- Détermine si le changement doit être vérifié.
- Passe l'appel à la sur-classe si le changement doit être vérifié.
12.7.5.3. Configurer l'auditing conditionnel.
Hibernate Envers persiste les données d'auditing en réaction à certains événements, en utilisant une série de listeners d'événements. Ces listeners sont enregistrés automatiquement si le jar Envers est sur le chemin de classe. Cette tâche couvre les étapes requises pour mettre en place l'auditing conditionnel, en remplaçant certains listeners d'événements Envers.
Procédure 12.10. Implémentation de l'Auditing conditionnel
- Définir la propriété Hibernate
hibernate.listeners.envers.autoRegister
à false dans le fichierpersistence.xml
. - Sous-classer chaque listener d'événement à remplacer. Mettez la logique d'auditing conditionnel dans la sous-classe, et appeler la super méthode si on doit procéder à l'auditing.
- Créer une implémentation personnalisée de
org.hibernate.integrator.spi.Integrator
, semblable àorg.hibernate.envers.event.EnversIntegrator
. Utiliser les sous-classes de listeners d'événements créées à la deuxième étape, à la place des classes par défaut. - Ajouter un fichier
META-INF/services/org.hibernate.integrator.spi.Integrator
au jar. Ce fichier doit contenir le nom complet de la classe qui implémente d'interface.
L'auditing conditionnel alors est configuré, remplaçant ainsi les listeners d'événements Envers par défaut.
12.7.5.4. Propriétés de configuration Envers
Tableau 12.12. Paramètres de configuration du Versioning de données d'entité
Nom de propriété | Valeur par défaut | Description |
---|---|---|
org.hibernate.envers.audit_table_prefix
| |
String attaché au nom d'une entité auditée, pour créer le nom de l'entité qui contiendra l'audit en formation.
|
org.hibernate.envers.audit_table_suffix
|
_AUD
|
String attaché au nom d'une entité auditée, pour créer le nom de l'entité qui contiendra l'audit en formation. Par exemple, si une entité ayant comme nom de tableau
Person est auditée, Envers créera un tableau nommé Person_AUD pour stocker les données historiques.
|
org.hibernate.envers.revision_field_name
|
REV
|
Le nom du champ qui contient le numéro de révision dans l'entité d'audit.
|
org.hibernate.envers.revision_type_field_name
|
REVTYPE
|
Le nom du champ de l'entité d'audit qui contient le type de révision. Les types actuels de révision possibles sont :
add , mod et del .
|
org.hibernate.envers.revision_on_collection_change
|
true
|
Cette propriété indique si une révision doit être créée si un champ relationnel qui n'appartient à personne change. Il peut s'agir d'une collection d'une relation one-to-many, ou bien du champ qui utilise l'attribut
mappedBy dans une relation one-to-one.
|
org.hibernate.envers.do_not_audit_optimistic_locking_field
|
true
|
Si true, les propriétés utilisées pour le verrouillage optimiste (annoté par
@Version ) sera exclus automatiquement de l'audit.
|
org.hibernate.envers.store_data_at_delete
|
false
|
Cette propriété définit si oui ou non les données d'entité doivent être stockées dans la révision lorsque l'entité est supprimée, au lieu de l'ID uniquement, avec toutes les autres propriétés marquées comme null. Ce n'est pas généralement nécessaire, car les données sont présentes dans l'avant dernière révision. Parfois, il est plus facile et plus efficace d'y accéder dans la dernière révision. Cependant, cela signifie que les données de l'entité contenues avant suppression apparaissent deux fois.
|
org.hibernate.envers.default_schema
|
null (comme dans les tableaux normaux)
|
Le nom de schéma par défaut utilisé pour les tableaux d'audit. Peut être substitué à l'aide de l'annotation
@AuditTable(schema="...") . Si absent, le schéma sera le même que le schéma des tableaux normaux.
|
org.hibernate.envers.default_catalog
|
null (comme dans les tableaux normaux)
|
Le nom de catalogue par défaut qui doit être utilisé dans les tableaux d'audit. Peut être substitué par l'annotation
@AuditTable(catalog="...") . Si non présent, le catalogue sera le même que le catalogue des tableaux normaux.
|
org.hibernate.envers.audit_strategy
|
org.hibernate.envers.strategy.DefaultAuditStrategy
|
Cette propriété définit la stratégie d'auditing qui doit être utilisée lors de la persistance des données d'audit. Par défaut, seule la révision où une entité a été modifiée est stockée. Alternativement,
org.hibernate.envers.strategy.ValidityAuditStrategy stocke la révision du départ et révision de la fin. Ceux-ci définissent ensemble quand une ligne d'auditing est valide.
|
org.hibernate.envers.audit_strategy_validity_end_rev_field_name
|
REVEND
|
Le nom de la colonne qui contiendra le dernier numéro de révision dans les entités d'auditing. Cette propriété n'est valide que si la stratégie d'auditing de validité est utilisée.
|
org.hibernate.envers.audit_strategy_validity_store_revend_timestamp
|
false
|
Cette propriété définit si l'horodatage du dernier numéro de révision des dernières données valides doit être conservé en plus de la dernière révision elle-même. Ceci est utile pour pouvoir purger les archives d'audit sur une base de données relationnelle en utilisant le partitionnement de la table. Le partitionnement exige une colonne qui existe dans la table. Cette propriété n'est évaluée que si la
ValidityAuditStrategy est utilisée
|
org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name
|
REVEND_TSTMP
|
Nom de la colonne d'horodatage de la dernière révision au moment où les données étaient encore valides. Utilisé uniquement si
ValidityAuditStrategy est utilisé, et si org.hibernate.envers.audit_strategy_validity_store_revend_timestamp évalue sur true.
|
12.7.6. Recherches
12.7.6.1. Suppression de l'information d'auditing
Hibernate Envers fournit une fonctionnalité qui permet d'extraire des informations d'audit par des requêtes. Cette section vous donne des exemples de telles requêtes.
Note
live
, car elles impliquent des sous-sélections en corrélation.
Exemple 12.33. Chercher des entités d'une classe pour une révision donnée
AuditQuery query = getAuditReader() .createQuery() .forEntitiesAtRevision(MyEntity.class, revisionNumber);
AuditEntity
. La recherche ci-dessous ne sélectionne que des entités dont la propriété nom
est égale à John
:
query.add(AuditEntity.property("name").eq("John"));
query.add(AuditEntity.property("address").eq(relatedEntityInstance)); // or query.add(AuditEntity.relatedId("address").eq(relatedEntityId));
List personsAtAddress = getAuditReader().createQuery() .forEntitiesAtRevision(Person.class, 12) .addOrder(AuditEntity.property("surname").desc()) .add(AuditEntity.relatedId("address").eq(addressId)) .setFirstResult(4) .setMaxResults(2) .getResultList();
Exemple 12.34. Chercher les révisions de demandes pour lesquelles les entités d'une classe donnée ont changé
AuditQuery query = getAuditReader().createQuery() .forRevisionsOfEntity(MyEntity.class, false, true);
AuditEntity.revisionNumber()
- Spécifie les contraintes, les projections et l'ordre en considération desquels l'entité auditée a été modifiée dans le numéro de révision.
AuditEntity.revisionProperty(propertyName)
- Spécifier les contraintes, les projections et l'ordre de l'entité de révision sur une propriété , correspondants à la révision dans laquelle l'entité auditée a été modifiée.
AuditEntity.revisionType()
- Fournit des accès au type de révision (ADD, MOD, DEL).
MyEntity
, ayant pour ID entityId
a changé, après le numéro de révision 42 :
Number revision = (Number) getAuditReader().createQuery() .forRevisionsOfEntity(MyEntity.class, false, true) .setProjection(AuditEntity.revisionNumber().min()) .add(AuditEntity.id().eq(entityId)) .add(AuditEntity.revisionNumber().gt(42)) .getSingleResult();
actualDate
d'une entité donnée est supérieure à une valeur donnée, mais aussi petite que possible :
Number revision = (Number) getAuditReader().createQuery() .forRevisionsOfEntity(MyEntity.class, false, true) // We are only interested in the first revision .setProjection(AuditEntity.revisionNumber().min()) .add(AuditEntity.property("actualDate").minimize() .add(AuditEntity.property("actualDate").ge(givenDate)) .add(AuditEntity.id().eq(givenEntityId))) .getSingleResult();
minimize()
et maximize()
renvoient un critère, auquel des contraintes peuvent être ajoutées, et qui doit être respecté par les entités avec des propriétés maximisées/minimisées.
selectEntitiesOnly
- Ce paramètre n'est valide que lorsqu'une projection explicite n'est pas définie.Si true, le résultat de la recherche correspondra à une liste d'entités qui ont changé lors de révisions satisfaisant les contraintes spécifiées.Si false, le résultat correspondra à une liste de trois tableaux d'éléments. Le premier élément correspondra à l'instance d'entité modifiée. Le second correspondra à une entité qui contient les données de révision. Si aucune entité personnalisée n'est utilisée, alors on aura une instance de
DefaultRevisionEntity
. Le troisième tableau d'éléments correspondra au type de révision (ADD, MOD, DEL). selectDeletedEntities
- Ce paramètre indique si les révisions dans lesquelles les entités ont été supprimées doivent être incluses dans les résultats. Si true, les entités auront le type de révision
DEL
, et tous les champs, à part l'id, auront pour valeurnull
.
Exemple 12.35. Chercher les révisions d'une entité qui ont pu modifier une propriété donnée
MyEntity
pour une id donnée, quand la propriété actualDate
a été modifiée.
AuditQuery query = getAuditReader().createQuery() .forRevisionsOfEntity(MyEntity.class, false, true) .add(AuditEntity.id().eq(id)); .add(AuditEntity.property("actualDate").hasChanged())
hasChanged
peut être combinée à des critères supplémentaires. La recherche ci-dessous renvoie une coupe horizontale de MyEntity
au moment où le revisionNumber est créé. Ce sera limité aux révisions qui ont modifié prop1
, et non pas prop2
.
AuditQuery query = getAuditReader().createQuery() .forEntitiesAtRevision(MyEntity.class, revisionNumber) .add(AuditEntity.property("prop1").hasChanged()) .add(AuditEntity.property("prop2").hasNotChanged());
MyEntities
modifiées dans le revisionNumber avec prop1
modifié et prop2
intact. »
forEntitiesModifiedAtRevision
:
AuditQuery query = getAuditReader().createQuery() .forEntitiesModifiedAtRevision(MyEntity.class, revisionNumber) .add(AuditEntity.property("prop1").hasChanged()) .add(AuditEntity.property("prop2").hasNotChanged());
Exemple 12.36. Recherche d'entités modifiées dans une révision donnée
Set<Pair<String, Class>> modifiedEntityTypes = getAuditReader() .getCrossTypeRevisionChangesReader().findEntityTypes(revisionNumber);
org.hibernate.envers.CrossTypeRevisionChangesReader
:
List<Object> findEntities(Number)
- Retourne des instantanés de toutes les entités d'auditing changées (ajoutées, mises à jour et supprimées) dans une révision donnée. Exécute des requêtes SQL
n + 1
, oùn
est un nombre de classes d'entités différentes modifiées dans la révision spécifiée. List<Object> findEntities(Number, RevisionType)
- Retourne des instantanés de toutes les entités d'auditing changées (ajoutées, mises à jour et supprimées) dans une révision donnée filtrée par type de modification. Exécute des requêtes SQL
n + 1
, oùn
est un nombre de classes d'entités différentes modifiées dans la révision spécifiée. Map<RevisionType, List<Object>> findEntitiesGroupByRevisionType(Number)
- Retourne une carte contenant les listes des instantanés d'entité regroupés par opération de modification (ajout, mise à jour et suppression). Exécute des requêtes SQL
3n + 1
, oùn
est un nombre de classes d'entités différentes modifiées au cours de la révision spécifiée.
12.8. Réglage de la performance
12.8.1. Algorithmes de chargement de lots alternatifs
- Niveau Par classeLors du chargement de données sur un niveau par classe, Hibernate, requiert la taille de lot de l'association à pré-charger lorsqu'il est interrogé. Par exemple, imaginons que lors de l'exécution, vous ayez 30 instances d'un objet
voiture
chargées dans la session. Chaque objetvoiture
appartient à un objetpropriétaire
. Si vous deviez parcourir tous les objetsvoiture
et appeler leurs propriétaires, par un chargementdifféré
(lazy loading), Hibernate émettra 30 instructions select - une pour chaque propriétaire. Il s'agit d'un goulot d'étranglement de performance.À la place, vous pouvez dire à Hibernate de charger à l'avance le prochain lot de propriétaires avant qu'ils aient été interrogés par une requête. Quand un objetowner
(propriétaire) est interrogé, Hibernate interrogera ces objets à plusieurs reprises dans le même énoncé SELECT.Le nombre d'objetsowner
(propriétaire) à interroger à l'avance dépend du paramètrebatch-size
spécifié en cours de configuration :<class name="owner" batch-size="10"></class>
Cela indique à Hibernate d'interroger au moins 10 objetsowner
au cas où ils seraient nécessaires dans un avenir proche. Lorsqu'un utilisateur interroge le propriétaireowner
decar A
, le propriétaireowner
decar B
peut déjà avoir été chargé dans un lot de chargement. Lorsque l'utilisateur a besoin du propriétaireowner
decar B
, au lieu d'aller à la base de données (et émettre une instruction SELECT), la valeur peut être récupérée de la session en cours.En plus du paramètrebatch-size
, Hibernate 4.2.0 a introduit un nouvel élément de configuration pour améliorer la performance de chargement de lots. La configuration s'appelleBatch Fetch Style
et est spécifiée par le paramètrehibernate.batch_fetch_style
.Il existe trois styles d'extraction de lots pris en charge : LEGACY, PADDED et DYNAMIC. Pour spécifier le style, utiliserorg.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE
.- LEGACY : dans le style hérité de chargement, une série de tailles de lots pré-construits basées
ArrayHelper.getBatchSizes(int)
sont utilisées. Les lots sont chargés à l'aide de la taille de lot de pré-construction suivante à partir du nombre d'identificateurs existants pouvant être mis en lot.En continuant avec l'exemple suivant, ayant une taillebatch-size
de 30, les tailles de lot pré-construits sont [30, 15, 10, 9, 8, 7, .., 1]. Si l'on tente de mettre en lots 29 identifiants, on obtiendra des lots de 15, 10 et 4. Il y aura 3 requêtes SQL correspondantes, chacune chargeant 15, 10 et 4 propriétaires de la base de données. - PADDED - similaire au style de chargement de classes LEGACY. Utilise toujours des tailles de lots pré-construits, mais utilise la taille de lot supérieure et remplit les espaces réservés de l'identifiant supplémentaire.Tout comme dans l'exemple ci-dessus, si 30 objets propriétaires doivent être initialisés, il ne pourra y avoir qu'une demande exécutée dans la base de données.Cependant, si 29 objets propriétaires doivent être initialisés, Hibernate n'exécutera qu'un seul énoncé select SQL de taille de lot 30, avec l'espace supplémentaire rempli par un identifiant répété.
- Dynamic - bien que toujours conforme à la limitation de la taille des lots, ce style de chargement dynamique de lots construit son énoncé SQL SELECT à l'aide du nombre réel d'objets à charger.Par exemple, pour 30 objets propriétaires, et une taille de lot maximale de 30, un appel pour récupérer 30 objets propriétaires entraînera une instruction SQL SELECT. Un appel pour récupérer 35 se traduira en deux énoncés SQL, ayant pour taille de lot 30 et 5, respectivement. Hibernate modifiera dynamiquement le second énoncé SQL pour maintenir à 5, le nombre requis, tout en restant sous la restriction de 30 pour la taille des lots. C'est différent de la version PADDED, car le deuxième SQL ne sera pas PADDED et à la différence du style LEGACY, il n'y a pas de taille fixe pour le second énoncé SQL - le second SQL est créé dynamiquement.Pour une requête inférieure à 30 identifiants, ce style ne chargera de façon dynamique que le nombre d'identifiants requis.
- Niveau Par collectionHibernate peut aussi mettre en lots des collections de chargement, en respectant la taille ou les styles des lots d'extraction suivant la liste de la section par-classe ci-dessus.Pour inverser l'exemple utilisé dans la section précédente, considérons que vous deviez charger tous les objets
car
(voiture) appartenant à chaque objetowner
(propriétaire). Si 10 objets propriétaireowner
sont chargés dans la session en cours, itérer tous les propriétaires va générer 10 énoncés SELECT, un pour chaque appel de méthode àgetCars()
. Si vous activez l'extraction de lot pour la collection de voitures dans le mappage du propriétaire, Hibernate pourra chercher ces collections à l'avance, comme illustré ci-dessous.<class name="Owner"><set name="cars" batch-size="5"></set></class>
Donc, avec une taille de lot égale à 5 et en utilisant le style de lot hérité pour charger 10 collections, Hibernate exécutera deux énoncés SELECT, récupérant chacun 5 collections.
12.8.2. Mise en cache de second niveau d'objets de référence pour les données non mutables
hibernate.cache.use_reference_entries
à true
. Par défaut, hibernate.cache.use_reference_entries
est défini à false
.
hibernate.cache.use_reference_entries
a pour valeur true
, un objet de données immuables qui n'a pas d'associations n'est pas copié dans le cache de second niveau, et seule sa référence y sera stockée.
Avertissement
hibernate.cache.use_reference_entries
a pour valeur true
, un objet de données immuables qui possède des associations sera copié dans le cache de second niveau.
Chapitre 13. Hibernate Search
13.1. Introduction à Hibernate Search
13.1.1. Hibernate Search
13.1.2. Premières étapes de Recherche Hibernate
- Voir Configuration dans le Guide de configuration et d'administration pour configurer la Hibernate Search.
13.1.3. Activer Hibernate Search dans Maven
hibernate-search-orm
:
<dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>4.6.0.Final-redhat-2</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <scope>provided</scope> </dependency> </dependencies>
13.1.4. Ajouter les annotations
example.Book
et example.Author
et vous souhaitez ajouter des fonctionnalités de recherche de texte libre à votre application pour pouvoir rechercher des livres.
Exemple 13.1. Livre et auteur d'entités avant l'ajout d'annotations spécifiques de Hibernate Search
package example; ... @Entity public class Book { @Id @GeneratedValue private Integer id; private String title; private String subtitle; @ManyToMany private Set<Author> authors = new HashSet<Author>(); private Date publicationDate; public Book() {} // standard getters/setters follow here ... }
package example; ... @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; public Author() {} // standard getters/setters follow here ... }
Book
et Author
. La première annotation @Indexed
désigne la classe Book
comme indexable. Hibernate Search stocke un ID sans token dans l'index pour s'assurer de l'unicité d'index d'une entité donnée. @DocumentId
indique la propriété à utiliser et est similaire, dans la plupart des cas, à la clé primaire de la base de données. L'annotation @DocumentId
est facultative dans le cas où une annotation @Id
existe.
title
et subtitle
et annotez-les avec @Field
. Le paramètre index=Index.YES
s'assurera que le texte sera indexé tandis que analyze=Analyze.YES
s'assure que le texte sera analysé en utilisant l'analyseur Lucene par défaut. L'analyse signifie généralement diviser une phrase en mots et exclure certains termes courants comme « a
» (un/une) ou « the
» (le/la). Nous abordons les analyseurs plus en détail par la suite. Le troisième paramètre que nous indiquons dans @Field
, store=Store.NO
, permet de garantir que les données ne seront pas stockées dans l'index. Le fait ou non que ces données soient stockées dans l'index n'aura aucun impact sur la possibilité de les rechercher. Du point de vue de Lucene, il n'est pas nécessaire de préserver les données une fois l'index créé. L'avantage de les stocker réside dans la capacité de les récupérer par projections ( voir Section 13.3.1.10.5, « Projection »).
index=Index.YES
, analyze=Analyze.YES
et store=Store.NO
sont les valeurs par défaut de ces paramètres et peuvent être omises.
@DateBridge
n'a pas encore été abordée. Cette annotation fait partie des ponts de champ intégrés dans Hibernate Search. L'index Lucene se base uniquement sur des chaînes. C'est pour cette raison que Hibernate Search doit convertir les types de données des champs indexés en chaîne et vice-versa. Une gamme de ponts prédéfinis est fournie, y compris la classe DateBridge
qui convertira une classe java.util.Date
en une classe String
avec la résolution indiquée. Pour plus de détails, veuillez consulter Section 13.2.4, « Ponts ».
@IndexedEmbedded.
Cette annotation est utilisée pour indexer des entités associées (@ManyToMany
, @*ToOne
, @Embedded
et @ElementCollection
) comme faisant partie de l'entité propriétaire, ce qui est nécessaire puisqu'un document d'index Lucene est une structure de données plate ne connaissant rien aux relations d'objets. Pour faire en sorte que le nom des auteurs soit trouvable, vous devez vous assurer que les noms sont indexés comme faisant partie du livre. En plus de @IndexedEmbedded
, vous devrez signaler tous les champs de l'entité associée que vous souhaitez inclure dans l'index comme @Indexed
. Pour plus d'informations, veuillez consulter Section 13.2.1.3, « Objets associés et intégrés »
Exemple 13.2. Les entités après l'ajout d'annotations de Hibernate Search
package example; ... @Entity @Indexed public class Book { @Id @GeneratedValue private Integer id; @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO) private String title; @Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO) private String subtitle; @Field(index = Index.YES, analyze=Analyze.NO, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; @IndexedEmbedded @ManyToMany private Set<Author> authors = new HashSet<Author>(); public Book() { } // standard getters/setters follow here ... }
package example;
...
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
@Field
private String name;
public Author() {
}
// standard getters/setters follow here
...
}
13.1.5. Indexation
Exemple 13.3. Indexation de données à l'aide d'une session Hibernate
FullTextSession fullTextSession = org.hibernate.search.Search.getFullTextSession(session); fullTextSession.createIndexer().startAndWait();
Exemple 13.4. Indexation de données à l'aide de JPA
EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em); fullTextEntityManager.createIndexer().startAndWait();
/var/lucene/indexes/example.Book
. Inspectez cet index avec Luke. Cela vous aidera à comprendre comment Hibernate Search fonctionne.
13.1.6. Recherche
org.hibernate.Query
pour obtenir les fonctionnalités requises de l'API Hibernate. Le code suivant prépare une requête contre les champs indexés. L'exécution du code renvoie une liste de Book
.
Exemple 13.5. Utiliser une session Hibernate Search pour créer et exécuter une recherche
FullTextSession fullTextSession = Search.getFullTextSession(session); Transaction tx = fullTextSession.beginTransaction(); // create native Lucene query using the query DSL // alternatively you can write the Lucene query using the Lucene query parser // or the Lucene programmatic API. The Hibernate Search DSL is recommended though QueryBuilder qb = fullTextSession.getSearchFactory() .buildQueryBuilder().forEntity( Book.class ).get(); org.apache.lucene.search.Query query = qb .keyword() .onFields("title", "subtitle", "authors.name", "publicationDate") .matching("Java rocks!") .createQuery(); // wrap Lucene query in a org.hibernate.Query org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery(query, Book.class); // execute search List result = hibQuery.list(); tx.commit(); session.close();
Exemple 13.6. Utilisation de JPA pour créer et exécuter une recherche
EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em); em.getTransaction().begin(); // create native Lucene query using the query DSL // alternatively you can write the Lucene query using the Lucene query parser // or the Lucene programmatic API. The Hibernate Search DSL is recommended though QueryBuilder qb = fullTextEntityManager.getSearchFactory() .buildQueryBuilder().forEntity( Book.class ).get(); org.apache.lucene.search.Query query = qb .keyword() .onFields("title", "subtitle", "authors.name", "publicationDate") .matching("Java rocks!") .createQuery(); // wrap Lucene query in a javax.persistence.Query javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(query, Book.class); // execute search List result = persistenceQuery.getResultList(); em.getTransaction().commit(); em.close();
13.1.7. Analyseur
Refactoring: Improving the Design of Existing Code
et que des clics sont nécessaires pour les requêtes suivantes : refactor
, refactors
, refactored
et refactoring
. Sélectionnez une classe d'analyseur dans Lucene qui applique l'analyse de radical d'un mot lors de l'indexation et de la recherche. Hibernate Search offre plusieurs façons de configurer l'analyseur (voir Section 13.2.3.1, « Analyseur par défaut et analyseur par classe » pour plus d'informations) :
- Définir la propriété
analyzer
dans le fichier de configuration. La classe spécifiée devient l'analyseur par défaut. - Définissez l'annotation
au niveau de l'entité.@Analyzer
- Définissez l'annotation
@
au niveau du champ.Analyzer
@AnalyzerDef
avec l'annotation @Analyzer
. Le framework de l'analyseur Solr et ses fabriques sont utilisés dans la dernière option. Pour plus d'informations sur les classes de fabrique, voir la JavaDoc Solrf ou lire un section correspondante Wiki Solr (http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters)
StandardTokenizerFactory
est utilisé par deux fabriques de filtre : LowerCaseFilterFactory
et SnowballPorterFilterFactory
. Le générateur de jetons divise les mots à chaque caractère de ponctuation et tiret mais ne touche pas aux adresses e-mail et noms d'hôtes d'internet intacts. Le générateur de jetons est idéal pour cela mais également pour d'autres opérations d'ordre général. Le filtre de bas-de-casse convertit toutes les lettres dans le jeton en bas-de-casse et le filtre boule de neige applique une recherche de radical linguistique.
Exemple 13.7. Utilisation de @AnalyzerDef et du cadre Solr pour définir et utiliser un analyseur
@Indexed @AnalyzerDef( name = "customanalyzer", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = { @Parameter(name = "language", value = "English") }) }) public class Book implements Serializable { @Field @Analyzer(definition = "customanalyzer") private String title; @Field @Analyzer(definition = "customanalyzer") private String subtitle; @IndexedEmbedded private Set authors = new HashSet(); @Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date publicationDate; public Book() { } // standard getters/setters follow here ... }
@AnalyzerDef
pour définir un analyseur, puis appliquez-le à des entités et propriétés à l'aide de @Analyzer
. Dans cet exemple, le customanalyzer
est défini mais pas appliqué à l'entité. L'analyseur est uniquement appliqué aux propriétés title
et subtitle
. Une définition d'analyseur est globale. Définissez l'analyseur pour une entité et réutilisez la définition pour d'autres entités si requis.
13.2. Mapper des entités dans la structure de l'index
13.2.1. Mappage d'une entité
13.2.1.1. Mappage de base
- @Indexed
- @Field
- @NumericField
- @Id
13.2.1.1.1. @Indexed
@Indexed
(toutes les entités non annotées avec @Indexed
seront ignorées par le processus d'indexation) :
Exemple 13.8. Rendre une classe indexable avec @Indexed
@Entity
@Indexed
public class Essay {
...
}
index
de l'annotation @Indexed pour changer le nom par défaut de l'index.
13.2.1.1.2. @Field
@Field
déclare une propriété comme indexée et autorise la configuration de plusieurs aspects du procédé d'indexation en définissant un ou plusieurs des attributs suivants :
name
: décrit le nom sous lequel la propriété doit être stockée dans le Document Lucene. La valeur par défaut est le nom de propriété (d'après la convention de JavaBeans)store
: indique si la propriété est stockée dans l'index Lucene. Vous pouvez stocker la valeurStore.YES
(utilise plus d'espace dans l'index mais permet une projection, voir Section 13.3.1.10.5, « Projection »), la stocker de manière compresséeStore.COMPRESS
(utilise davantage de CPU), ou refuser tout stockageStore.NO
(valeur par défaut). Lorsqu'une propriété est stockée, vous pouvez récupérer sa valeur initiale à partir du document Lucene, indépendamment de l'indexation de l'élément.index
: décrit si la propriété est indexée. Les valeurs disponibles sontIndex.NO
(aucune indexation, la propriété ne peut être trouvée par une requête),Index.YES
(l'élement est indexé et peut être recherché). La valeur par défaut estIndex.YES
.Index.NO
peut être utile lorsqu'une propriété n'a pas l'obligation d'être trouvable mais doit être disponible pour une projection.Note
Index.NO
en association avecAnalyze.YES
ouNorms.YES
est inutile caranalyze
etnorms
requièrent que la propriété soit indexée.analyze
: détermine si la propriété est analysée (Analyze.YES
) ou pas (Analyze.NO
). La valeur par défaut estAnalyze.YES
.Note
Que vous souhaitiez ou non analyser une propriété dépendra de si vous souhaitez rechercher l'élément tel quel, ou par les mots qu'il contient. Il est utile d'analyser un champ de texte, mais peut-être pas un champ de date.Note
Les champs utilisés pour trier ne doivent pas être analysés.norms
: décrit si l'information concernant le boosting de temps d'indexation doit être stocké (Norms.YES
) ou pas (Norms.NO
). Ne pas la stocker peut économiser une large partie de la mémoire, mais aucune information concernant le boosting de temps d'indexation ne sera disponible. La valeur par défaut estNorms.YES
.termVector
: décrit des collections de paires de fréquence d'un terme. Cet attribut permet de stocker les vecteurs de terme dans les documents au cours de l'indexation. La valeur par défaut estTermVector.NO
.Les différentes valeurs de cet attribut sont :Valeur Définition TermVector.YES Stocke les vecteurs de terme de chaque document et crée deux matrices synchronisées, une contenant des termes de document et l'autre contenant la fréquence d'un terme. TermVector.NO Ne pas stocker de vecteurs de terme. TermVector.WITH_OFFSETS Stocke le vecteur de terme et les informations de décalage de jeton. Identique à TermVector.YES mais contient également les informations de position de décalage de démarrage et de fin pour les termes. TermVector.WITH_POSITIONS Stocke le vecteur de terme et les informations de position de jeton. Identique à TermVector.YES mais contient également les positions ordinales de chaque occurrence d'un terme dans un document. TermVector.WITH_POSITION_OFFSETS Stocke le vecteur de terme, la position du jeton et les informations de décalage. Un mélange de YES, WITH_OFFSETS et WITH_POSITIONS. indexNullAs
: par défaut, les valeurs nulles sont ignorées et pas indexées. Cependant, l'utilisation deindexNullAs
vous permet de spécifier une chaîne qui sera insérée comme jeton pour la valeurnull
. Cette valeur est définie par défaut commeField.DO_NOT_INDEX_NULL
, indiquant que les valeursnull
ne devraient pas être indexées. Vous pouvez définir cette valeur commeField.DEFAULT_NULL_TOKEN
pour indiquer qu'un jetonnull
par défaut doit être utilisé. Ce jetonnull
par défaut peut être spécifié dans la configuration en utilisanthibernate.search.default_null_token
. Si cette propriété n'est pas définie et que vous spécifiezField.DEFAULT_NULL_TOKEN
, la chaîne "_null_" sera utilisée par défaut.Note
Lorsque le paramètreindexNullAs
est utilisé, il est important d'utiliser le même jeton dans la requête de recherche pour rechercher les valeursnull
. Il est également recommandé d'utiliser cette fonctionnalité uniquement avec des champs non-analysés (
).analyze=
Analyze.NOAvertissement
Lors de l'implémentation d'une classeFieldBridge
ouTwoWayFieldBridge
personnalisée, il appartient au développeur de gérer l'indexation des valeurs nulles (voir JavaDocs deLuceneOptions.indexNullAs()
).
13.2.1.1.3. @NumericField
@Field
appelée @NumericField
pouvant être spécifiée dans la même étendue que @Field
ou @DocumentId
. Elle peut être spécifiée pour les propriétés Integer, Long, Float et Double. Au moment de l'indexation, la valeur sera indexée à l'aide d'une structure Trie. Une propriété indexée comme champ numérique plutôt que propriété standard permet une requête de données plus rapide que sur des propriétés @Field
standards. L'annotation @NumericField
accepte les paramètres suivants :
Valeur | Définition |
---|---|
forField | (Facultatif) Spécifier le nom du @Field correspondant qui sera indexé comme numérique. Cela est uniquement obligatoire lorsque la propriété contient plus d'une déclaration @Field. |
precisionStep | (Facultatif) Change la manière dont la structure « Trie » est stockée dans l'index. Des étapes de précision plus petites mènent à davantage d'utilisation d'espace disque et des requêtes de trie et de gamme plus rapides. Des valeurs supérieures conduisent à un espace utilisé moindre et à une performance de requête de gamme plus proche de la requête de gamme dans les @Fields normaux. La valeur par défaut est 4. |
@NumericField
prend uniquement en charge Double
, Long
, Integer
et Float
. Il n'est pas possible de prendre un avantage d'une fonctionnalité similaire dans Lucene pour les autres types numériques. Les types restants doivent donc utiliser la chaîne dont le codage est effectué par la classe TwoWayFieldBridge
par défaut ou personnalisée.
NumericFieldBridge
personnalisée si vous savez gérer l'approximation lors de la transformation du type :
Exemple 13.9. Définition d'une classe NumericFieldBridge
personnalisée
public class BigDecimalNumericFieldBridge extends NumericFieldBridge { private static final BigDecimal storeFactor = BigDecimal.valueOf(100); @Override public void set(String name, Object value, Document document, LuceneOptions luceneOptions) { if ( value != null ) { BigDecimal decimalValue = (BigDecimal) value; Long indexedValue = Long.valueOf( decimalValue.multiply( storeFactor ).longValue() ); luceneOptions.addNumericFieldToDocument( name, indexedValue, document ); } } @Override public Object get(String name, Document document) { String fromLucene = document.get( name ); BigDecimal storedBigDecimal = new BigDecimal( fromLucene ); return storedBigDecimal.divide( storeFactor ); } }
13.2.1.1.4. @Id
id
(identifiant) d'une entité est une propriété spéciale utilisée par Hibernate Search pour assurer l'unicité d'index d'une entité donnée. Un id
doit être stocké et ne pas être segmenté. Pour identifier une propriété comme identifiant d'index, utilisez l'annotation @DocumentId
. Si vous utilisez JPA et que vous avez spécifié @Id
, vous pouvez omettre @DocumentId
. L'identifiant d'entité choisi sera également utilisé comme identifiant de document.
Exemple 13.10. Spécification des propriétés indexées
@Entity @Indexed public class Essay { ... @Id @DocumentId public Long getId() { return id; } @Field(name="Abstract", store=Store.YES) public String getSummary() { return summary; } @Lob @Field public String getText() { return text; } @Field @NumericField( precisionStep = 6) public float getGrade() { return grade; } }
id
, Abstract
, text
et grade
. Veuillez noter que par défaut, le nom de champ ne contient pas de majuscules, d'après les normes JavaBean. Le champ grade
est annoté comme numérique avec une étape de précision légèrement plus large que celle par défaut.
13.2.1.2. Mapper des propriétés plusieurs fois
Exemple 13.11. Utilisation de @Fields pour mapper une propriété plusieurs fois
@Entity @Indexed(index = "Book" ) public class Book { @Fields( { @Field, @Field(name = "summary_forSort", analyze = Analyze.NO, store = Store.YES) } ) public String getSummary() { return summary; } ... }
summary
est indexé deux fois, la première fois comme summary
avec des jetons, puis comme summary_forSort
sans jetons.
13.2.1.3. Objets associés et intégrés
address.city:Atlanta
). Les champs de lieu seront indexés dans l'index Place
. Les documents de l'index Place
contiendront également les champs address.id
, address.street
et address.city
pour lesquels vous pourrez faire une requête.
Exemple 13.12. Associations d'indexation
@Entity @Indexed public class Place { @Id @GeneratedValue @DocumentId private Long id; @Field private String name; @OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } ) @IndexedEmbedded private Address address; .... } @Entity public class Address { @Id @GeneratedValue private Long id; @Field private String street; @Field private String city; @ContainedIn @OneToMany(mappedBy="address") private Set<Place> places; ... }
@IndexedEmbedded
, Hibernate Search doit être informé de tout changement dans les objets Place
et Address
afin de maintenir l'index à jour. Pour garantir que le document Lucene Place
est mis à jour lorsque son Address
est modifiée, indiquez @ContainedIn
de l'autre côté de la relation bidirectionnelle.
Note
@ContainedIn
est utile pour les deux associations indiquant les entités ainsi que pour les (collections d') objets intégrés.
Exemple 13.13. Utilisation imbriquée de @IndexedEmbedded
et @ContainedIn
@Entity @Indexed public class Place { @Id @GeneratedValue @DocumentId private Long id; @Field private String name; @OneToOne( cascade = { CascadeType.PERSIST, CascadeType.REMOVE } ) @IndexedEmbedded private Address address; .... } @Entity public class Address { @Id @GeneratedValue private Long id; @Field private String street; @Field private String city; @IndexedEmbedded(depth = 1, prefix = "ownedBy_") private Owner ownedBy; @ContainedIn @OneToMany(mappedBy="address") private Set<Place> places; ... } @Embeddable public class Owner { @Field private String name; ... }
@*ToMany, @*ToOne
et @Embedded
peut être annoté par @IndexedEmbedded
. Les attributs de la classse associée seront ensuite ajoutés à l'index d'entités principal. Dans l'Exemple 13.13, « Utilisation imbriquée de @IndexedEmbedded
et @ContainedIn
», l'index contiendra les champs suivants :
- id
- name
- address.street
- address.city
- address.ownedBy_name
propertyName.
, suivi de la convention de navigation d'objet traditionnelle. Vous pouvez le remplacer à l'aide de l'attribut prefix
tel que démontré dans la propriété ownedBy
.
Note
depth
est nécessaire lorsque le graphique de l'objet comprend une dépendance cyclique de classes (non d'instances). Par exemple, si Owner
indique Place
. Hibernate Search arrêtera d'inclure les attributs intégrés indexés une fois la profondeur souhaitée atteinte (ou dès que les limites du graphique de l'objet sont atteintes). Une classe possédant une auto-référence est un exemple de dépendance cyclique. Dans notre exemple, depth
étant défini sur 1, tout attribut @IndexedEmbedded
dans Owner (le cas échéant) sera ignoré.
@IndexedEmbedded
pour les associations d'objets vous permet d'exprimer des requêtes (à l'aide de la syntaxe de requête de Lucene) telles que :
- Renvoyer des lieux dont le nom contient JBoss et dont l'adresse se trouve à Atlanta. En requête Lucene, cela s'apparente à :
+name:jboss +address.city:atlanta
- Renvoyer des lieux dont le nom contient JBoss et dont le nom du propriétaire contient Joe. En requête Lucene, cela s'apparente à :
+name:jboss +address.ownedBy_name:joe
Note
@Indexed
@ContainedIn
(tel que dans l'exemple précédent). Sinon, Hibernate Search n'a aucun moyen de mettre à jour l'index de root quand l'entité associée est mise à jour (dans notre exemple, un document index Place
doit être mis à jour lorsque l'instance Address
associée est mise à jour).
@IndexedEmbedded
n'est pas le type d'objet ciblé par Hibernate et Hibernate Search. Cela est particulièrement le cas lorsque les interfaces sont utilisées au lieu de leur implémentation. Vous pouvez pour cette raison remplacer le type d'objet ciblé par Hibernate Search à l'aide du paramètre targetElement
.
Exemple 13.14. Utilisation de la propriété targetElement
de @IndexedEmbedded
@Entity
@Indexed
public class Address {
@Id
@GeneratedValue
@DocumentId
private Long id;
@Field
private String street;
@IndexedEmbedded(depth = 1, prefix = "ownedBy_", targetElement = Owner.class)
@Target(Owner.class)
private Person ownedBy;
...
}
@Embeddable
public class Owner implements Person { ... }
13.2.1.4. Limitation de l'intégration d'objets à des chemins spécifiques
@IndexedEmbedded
fournit également un attribut includePaths
pouvant être utilisé comme alternative à ou en association avec la méthode depth
(profondeur).
depth
est utilisé, tous les champs indexés du type intégré seront ajoutés de manière récursive à la même profondeur. Cela complique la sélection d'un seul chemin spécifique sans ajouter tous les autres champs, qui ne sont pas forcément utiles.
includePaths
de @IndexedEmbedded
».
Exemple 13.15. Utilisation de la propriété includePaths
de @IndexedEmbedded
@Entity
@Indexed
public class Person {
@Id
public int getId() {
return id;
}
@Field
public String getName() {
return name;
}
@Field
public String getSurname() {
return surname;
}
@OneToMany
@IndexedEmbedded(includePaths = { "name" })
public Set<Person> getParents() {
return parents;
}
@ContainedIn
@ManyToOne
public Human getChild() {
return child;
}
...//other fields omitted
includePaths
de @IndexedEmbedded
» vous permet de rechercher une classe Person
par name
(prénom) et/ou surname
(nom de famille), et/ou le name
d'un parent. Le surname
du parent ne sera pas indexé, la recherche des noms de famille d'un parent sera donc impossible mais cela accélèrera l'indexation, économisera de l'espace et améliorera la performance générale.
includePaths
de @IndexedEmbedded
incluera les chemins spécifiés en plus de que vous indexeriez normalement en spécifiant une valeur limitée pour depth
. Lorsque vous utilisez includePaths
et laissez depth
indéfini, le comportement est équivalent à une configuration depth
=0
: seuls les chemins inclus sont indexés.
Exemple 13.16. Utilisation de la propriété includePaths
de @IndexedEmbedded
@Entity
@Indexed
public class Human {
@Id
public int getId() {
return id;
}
@Field
public String getName() {
return name;
}
@Field
public String getSurname() {
return surname;
}
@OneToMany
@IndexedEmbedded(depth = 2, includePaths = { "parents.parents.name" })
public Set<Human> getParents() {
return parents;
}
@ContainedIn
@ManyToOne
public Human getChild() {
return child;
}
...//other fields omitted
includePaths
de @IndexedEmbedded
», chaque humain aura les attributs de son nom et nom de famille indexés. Le nom et nom de famille des parents sera également indexé, de manière récursive jusqu'à la seconde ligne à cause de l'attribut de la méthode depth
. Il sera possible de rechercher par nom et nom de famille, de la personne directement, de ses parents ou grands-parents. Au-delà du second niveau, nous indexerons également un niveau supplémentaire mais uniquement le nom, pas le nom de famille.
id
: comme clé primaire_hibernate_class
: stocke le type d'entiténame
: comme champ directsurname
: comme champ directparents.name
: comme champ intégré à profondeur 1parents.surname
: comme champ intégré à profondeur 1parents.parents.name
: comme champ intégré à profondeur 2parents.parents.surname
: comme champ intégré à profondeur 2parents.parents.parents.name
: comme chemin supplémentaire tel que spécifié parincludePaths
. Le premierparents.
est déduit à partir du nom de champ, le reste du chemin est l'attribut deincludePaths
13.2.2. Boosting
13.2.2.1. Boosting de temps d'indexation statique
@Boost
. Vous pouvez utiliser cette annotation dans @Field ou l'indiquer directement au niveau méthode ou classe.
Exemple 13.17. Les différentes façons d'utiliser @Boost
@Entity @Indexed @Boost(1.7f) public class Essay { ... @Id @DocumentId public Long getId() { return id; } @Field(name="Abstract", store=Store.YES, boost=@Boost(2f)) @Boost(1.5f) public String getSummary() { return summary; } @Lob @Field(boost=@Boost(1.2f)) public String getText() { return text; } @Field public String getISBN() { return isbn; } }
Essay
's d'atteindre le haut de la liste de recherche seront multipliées par 1.7. Le champ summary
sera 3 fois (2 x 1.5, car @Field.boost
et @Boost
sont cumulatifs sur une propriété) plus important que le champ isbn
. Le champ text
sera 1,2 fois plus important que le champ isbn
. Veuillez noter que cette explication est fausse en théorie, mais elle est simple et proche de la réalité en pratique.
13.2.2.2. Boosting de temps d'indexation dynamique
@Boost
utilisée dans Section 13.2.2.1, « Boosting de temps d'indexation statique » définit un facteur de boost statique indépendant du statut de l'entité indexée au temps d'exécution. Il existe cependant des cas d'utilisation dans lesquels le facteur de boosting peut dépendre du statut actuel de l'entité. Vous pouvez alors utiliser l'annotation @DynamicBoost
avec une BoostStrategy
personnalisée.
Exemple 13.18. Exemple de boosting dynamique
public enum PersonType { NORMAL, VIP } @Entity @Indexed @DynamicBoost(impl = VIPBoostStrategy.class) public class Person { private PersonType type; // .... } public class VIPBoostStrategy implements BoostStrategy { public float defineBoost(Object value) { Person person = ( Person ) value; if ( person.getType().equals( PersonType.VIP ) ) { return 2.0f; } else { return 1.0f; } } }
VIPBoostStrategy
comme implémentation de l'interface BoostStrategy
à utiliser au moment de l'indexation. Vous pouvez placer @DynamicBoost
au niveau class ou au niveau champ. Selon l'emplacement de l'annotation, l'entité entière passera à la méthode defineBoost
, ou uniquement la valeur de champ/propriété annotée. C'est à vous d'envoyer l'objet passé au type approprié. Dans cet exemple, toutes les valeurs indexées d'une personne VIP seront deux fois plus importantes que les valeurs d'une personne normale.
Note
BoostStrategy
spécifiée doit définir un constructeur public no-arg par défaut.
@Boost
et @DynamicBoost
comme vous le souhaitez dans votre entité. Tous les facteurs boost définis sont cumulatifs.
13.2.3. Analyse
Analysis
, qui consiste à convertir un texte en termes uniques (mots), est considéré comme l'une des fonctionnalités les plus importantes d'un moteur de recherche en texte intégral. Lucene utilise le concept des Analyzer
s pour contrôler ce procédé. La section suivante décrit les différentes façons offertes par Hibernate Search pour configurer ces analyseurs.
13.2.3.1. Analyseur par défaut et analyseur par classe
hibernate.search.analyzer
. La valeur par défaut pour cette propriété est org.apache.lucene.analysis.standard.StandardAnalyzer
.
Exemple 13.19. Les différentes façons d'utiliser @Analyzer
@Entity @Indexed @Analyzer(impl = EntityAnalyzer.class) public class MyEntity { @Id @GeneratedValue @DocumentId private Integer id; @Field private String name; @Field @Analyzer(impl = PropertyAnalyzer.class) private String summary; @Field( analyzer = @Analyzer(impl = FieldAnalyzer.class ) private String body; ... }
EntityAnalyzer
est utilisé pour indexer la propriété segmentée (name
), sauf summary
et body
qui sont indexés respectivement avec PropertyAnalyzer
et FieldAnalyzer
.
Avertissement
13.2.3.2. Analyseurs nommés
@Analyzer
et est composée de :
- un nom : la chaîne unique utilisée pour faire référence à la définition
- une liste de filtres de caractères : chaque filtre de caractère est chargé d'effectuer un pré-traitement des caractères d'entrée avant la génération de jetons. Les filtres de caractère servent à ajouter, modifier, supprimer ou normaliser des caractères.
- un générateur de jetons : sert à découper le flux d'entrée en mots individuels
- une liste de filtres : chaque filtre permet de supprimer, modifier ou même ajouter des mots dans le flux fourni par le générateur de jetons.
Tokenizer
démarre la génération de jetons en changeant l'entrée de caractère en jetons qui seront ensuite traités par les TokenFilter
s. Hibernate Search prend en charge cette infrastructure en utilisant le framework d'analyseur Solr.
Note
lucene-snowball
et pour la classe PhoneticFilterFactory
, vous aurez besoin du JAR commons-codec. Votre distribution de Hibernate Search fournit ces dépendances dans son répertoire lib/optional
.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-analyzers</artifactId> <version>4.6.0.Final-redhat-2</version> <scope>provided</scope> <dependency>
@AnalyzerDef
et le framework Solr ». Un filtre de caractère est d'abord défini par sa fabrique. Dans notre exemple, nous utilisons un filtre de caractère de mappage, et les caractères seront remplacés dans l'entrée basée sur les règles spécifiées dans le fichier de mappage. Puis un générateur de jetons est défini. Cet exemple utilise le générateur de jetons standard. Pour finir, une liste de filtres est définie par leur fabrique. Dans notre exemple, le filtre StopFilter
est généré en lisant le fichier de propriété du mot en question. Le filtre ignore la casse.
Exemple 13.20. @AnalyzerDef
et le framework Solr
@AnalyzerDef(name="customanalyzer", charFilters = { @CharFilterDef(factory = MappingCharFilterFactory.class, params = { @Parameter(name = "mapping", value = "org/hibernate/search/test/analyzer/solr/mapping-chars.properties") }) }, tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class), @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = StopFilterFactory.class, params = { @Parameter(name="words", value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ), @Parameter(name="ignoreCase", value="true") }) }) public class Team { ... }
Note
@AnalyzerDef
. L'ordre est important.
resource_charset
.
Exemple 13.21. Utilisez un jeu de caractères spécifique pour charger le fichier de propriété.
@AnalyzerDef(name="customanalyzer", charFilters = { @CharFilterDef(factory = MappingCharFilterFactory.class, params = { @Parameter(name = "mapping", value = "org/hibernate/search/test/analyzer/solr/mapping-chars.properties") }) }, tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class), @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = StopFilterFactory.class, params = { @Parameter(name="words", value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ), @Parameter(name="resource_charset", value = "UTF-16BE"), @Parameter(name="ignoreCase", value="true") }) }) public class Team { ... }
@Analyzer
tel que dans l'Exemple 13.22, « Référencer un analyseur par nom. ».
Exemple 13.22. Référencer un analyseur par nom.
@Entity
@Indexed
@AnalyzerDef(name="customanalyzer", ... )
public class Team {
@Id
@DocumentId
@GeneratedValue
private Integer id;
@Field
private String name;
@Field
private String location;
@Field
@Analyzer(definition = "customanalyzer")
private String description;
}
@AnalyzerDef
sont également disponibles par leur nom dans la classe SearchFactory
, ce qui est utile lors de la génération de requêtes.
Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer("customanalyzer");
13.2.3.3. Analyseurs disponibles
Tableau 13.1. Exemple de filtres de caractères disponibles
Fabrique | Description | Paramètres | Dépendances supplémentaires |
---|---|---|---|
MappingCharFilterFactory | Remplace un ou plusieurs caractères par un ou plusieurs caractères, selon les mappages spécifiés dans le fichier ressource. | mapping : dirige vers un fichier ressource contenant les mappages utilisant le format :
| aucun |
HTMLStripCharFilterFactory | Supprimer les balises HTML ordinaires, en gardant le texte | aucun | aucun |
Tableau 13.2. Exemple de générateurs disponibles
Fabrique | Description | Paramètres | Dépendances supplémentaires |
---|---|---|---|
StandardTokenizerFactory | Utiliser le générateur de jetons standard de Lucene | aucun | aucun |
HTMLStripCharFilterFactory | Supprimer les balises HTML, garder le texte et le passer à un StandardTokenizer (Générateur de jetons standard). | aucun | solr-core |
PatternTokenizerFactory | Divise le texte selon le modèle d'expression régulière spécifié. | pattern : l'expression régulière à utiliser pour générer des jetons
group : indique le groupe de modèle à extraire en jetons
| solr-core |
Tableau 13.3. Exemples de filtres disponibles
Fabrique | Description | Paramètres | Dépendances supplémentaires |
---|---|---|---|
StandardFilterFactory | Supprimer les points des acronymes et les apostrophes des mots. | aucun | solr-core |
LowerCaseFilterFactory | Mettre tous les termes en minuscules | aucun | solr-core |
StopFilterFactory | Supprime les mots (jetons) correspondant à une liste de mots vides | words : dirige vers un fichier ressource contenant les mots vides
ignoreCase : true (vrai) si la
casse doit être ignoré lors de la comparaison des mots vides, false (faux) le reste du temps
| solr-core |
SnowballPorterFilterFactory | Réduit un mot à sa racine dans une langue donnée. Par exemple, les termes protéger, protège et protection partagent la même racine. Ce filtre permet d'obtenir des résultats de recherche contenant des termes correspondants. | language : allemand, anglais, danois, espagnol, finnois, français, italien, norvégien, portugais, russe, suédois et autres | solr-core |
ISOLatin1AccentFilterFactory | Supprimer les accents pour les langues telles que le français | aucun | solr-core |
PhoneticFilterFactory | Insère des jetons phonétiquement similaires dans le flux de jetons | encoder : un de DoubleMetaphone, Metaphone, Soundex ou RefinedSoundex
injecter :
true ajoutera des jetons au flux, false remplacera le jeton existant
maxCodeLength : définit la longueur maximale du code à générer. Pris en charge uniquement pour les codages Metaphone et DoubleMetaphone
| solr-core et commons-codec |
CollationKeyFilterFactory | Convertit chaque jeton en son java.text.CollationKey puis code la classe CollationKey avec IndexableBinaryStringTools , pour lui permettre d'être stocké comme terme d'index. | custom , language , country , variant , strength , decomposition . Pour plus d'informations, veuillez consulter les javadocs CollationKeyFilter de Lucene. | solr-core et commons-io |
org.apache.solr.analysis.TokenizerFactory
et org.apache.solr.analysis.TokenFilterFactory
dans votre IDE pour voir les implémentations disponibles.
13.2.3.4. Sélection d'analyseur dynamique
BlogEntry
, l'analyseur peut dépendre de la propriété de la langue de l'entrée. Selon la propriété, la bonne racinisation linguistique doit être choisie pour indexer le texte.
AnalyzerDiscriminator
. Exemple 13.23, « Utilisation de @AnalyzerDiscriminator » décrit l'utilisation de cette annotation.
Exemple 13.23. Utilisation de @AnalyzerDiscriminator
@Entity @Indexed @AnalyzerDefs({ @AnalyzerDef(name = "en", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = EnglishPorterFilterFactory.class ) }), @AnalyzerDef(name = "de", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = GermanStemFilterFactory.class) }) }) public class BlogEntry { @Id @GeneratedValue @DocumentId private Integer id; @Field @AnalyzerDiscriminator(impl = LanguageDiscriminator.class) private String language; @Field private String text; private Set<BlogEntry> references; // standard getter/setter ... }
public class LanguageDiscriminator implements Discriminator { public String getAnalyzerDefinitionName(Object value, Object entity, String field) { if ( value == null || !( entity instanceof Article ) ) { return null; } return (String) value; } }
@AnalyzerDiscriminator
, tous les analyseurs à utiliser dynamiquement doivent être prédéfinis dans les définitions @AnalyzerDef
. Si tel est le cas, l'annotation @AnalyzerDiscriminator
peut être placée sur une classe ou sur une propriété spécifique de l'entité pour laquelle sélectionner un analyseur. Le paramètre impl
de AnalyzerDiscriminator
vous permet de spécifier une mise en œuvre concrète de l'interface Discriminator
. Il vous revient de fournir une mise en œuvre pour cette interface. La seule méthode à mettre en œuvre est getAnalyzerDefinitionName()
, utilisée à chaque champ ajouté au document Lucene. L'entité indexée passe également par la méthode d'interface. Le paramètre value
n'est déterminé que si AnalyzerDiscriminator
est placé au niveau propriété plutôt que classe. Dans ce cas là, la valeur représente la valeur actuelle de cette propriété.
Discriminator
doit renvoyer le nom d'une définition d'analyseur existante ou null
si l'analyseur par défaut ne doit pas être remplacé. Exemple 13.23, « Utilisation de @AnalyzerDiscriminator » part du principe que le paramètre de langue est 'de' ou 'en' correspondant aux noms spécifiés dans @AnalyzerDef
s.
13.2.3.5. Récupération d'un analyseur
Note
Exemple 13.24. Utilisation de l'analyseur étendu lors de la création d'une requête de texte intégral
org.apache.lucene.queryParser.QueryParser parser = new QueryParser( "title", fullTextSession.getSearchFactory().getAnalyzer( Song.class ) ); org.apache.lucene.search.Query luceneQuery = parser.parse( "title:sky Or title_stemmed:diamond" ); org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Song.class ); List result = fullTextQuery.list(); //return a list of managed objects
title
et un analyseur de radical est utilisé dans le champ title_stemmed
. En utilisant l'analyseur fourni par la fabrique de recherche, la requête utilise l'analyseur approprié selon le champ ciblé.
Note
@AnalyzerDef
par le nom de leur définition en utilisant searchFactory.getAnalyzer(String)
.
13.2.4. Ponts
@Field
doivent être converties en chaînes à indexer. La raison pour laquelle ce détail n'a pas été mentionné jusqu'à présent est que pour la plupart de vos propriétés Hibernate Search effectue la traduction pour vous grâce à un ensemble de ponts intégrés. Cependant, il vous faudra quelque fois exercer un contrôle plus détaillé du procédé de traduction.
13.2.4.1. Ponts intégrés
- null
- Par défaut, les éléments
null
ne sont pas indexés. Lucene ne prend pas en charge les éléments nuls. Il peut cependant être utile dans certaines situations d'insérer un jeton personnalisé représentant la valeurnull
. Pour plus d'informations, veuillez consulter Section 13.2.1.1.2, « @Field ». - java.lang.String
- Les chaînes sont indexées telles quelles
- short, Short, integer, Integer, long, Long, float, Float, double, Double, BigInteger, BigDecimal (court, Court, entier, Entier, long, Long, flottant, Flottant, double, Double, BigInteger, BigDecimal)
- Les nombres sont convertis en leur représentation de chaîne. Veuillez noter que les nombres ne peuvent pas être comparés par Lucene (c'est-à-dire utilisés en requêtes de plage de données) immédiatement : ils doivent être remplis.
Note
L'utilisation d'une requête de plage de données présente des inconvénients. Une alternative consiste à utiliser une requête de filtre qui filtrera la requête de résultat à la plage qui convient.Hibernate Search prend également en charge l'utilisation d'un StringBridge personnalisé tel que décrit dans la Section 13.2.4.2, « Ponts personnalisés ». - java.util.Date
- Les dates suivent le modèle yyyyMMddHHmmssSSS sur le fuseau horaire GMT (par exemple, 200611072203012 pour le 7 novembre 2006 à 16h03 et 12ms EST). Le format interne n'est pas très important. Ce qui compte, c'est que lorsque vous utilisez un TermRangeQuery, vous devez savoir que les dates doivent être exprimées en heure GMT.Il n'est généralement pas nécessaire d'enregistrer la date à la milliseconde près.
@DateBridge
définit la résolution que vous souhaitez enregistrer dans l'index (@DateBridge(resolution=Resolution.DAY)
). Le modèle de date sera ensuite tronqué en conséquence.@Entity @Indexed public class Meeting { @Field(analyze=Analyze.NO) @DateBridge(resolution=Resolution.MINUTE) private Date date; ...
Avertissement
Une date dont la résolution est inférieure àMILLISECOND
ne peut pas être un@DocumentId
Important
Le pont par défautDate
utilise lesDateTools
de Lucene pour convertir depuis et versString
. Cela signifie que toutes les dates sont exprimées en heure GMT. Si vous devez enregistrer vos dates dans un fuseau horaire défini, vous devrez alors mettre en œuvre un pont de date personnalisé. Veuillez vous assurer de comprendre les pré-requis de vos applications en ce qui concerne l'indexation et recherche de date. - java.net.URI, java.net.URL
- Les URI et URL sont convertis à leur représentation de chaîne.
- java.lang.Class
- Les classes sont converties à leur nom de classe entier. Le chargeur de classe de contexte de thread est utilisé lorsque la classe est réhydratée.
13.2.4.2. Ponts personnalisés
13.2.4.2.1. StringBridge
Object
attendu au String
que vous souhaitez. Pour ce faire, veuillez mettre en œuvre l'interface org.hibernate.search.bridge.StringBridge
. Toutes les implémentations doivent être thread-safe à cause de la nature simultanée de leur utilisation.
Exemple 13.25. Implémentation de StringBridge
personnalisée
/** * Padding Integer bridge. * All numbers will be padded with 0 to match 5 digits * * @author Emmanuel Bernard */ public class PaddedIntegerBridge implements StringBridge { private int PADDING = 5; public String objectToString(Object object) { String rawInteger = ( (Integer) object ).toString(); if (rawInteger.length() > PADDING) throw new IllegalArgumentException( "Try to pad on a number too big" ); StringBuilder paddedInteger = new StringBuilder( ); for ( int padIndex = rawInteger.length() ; padIndex < PADDING ; padIndex++ ) { paddedInteger.append('0'); } return paddedInteger.append( rawInteger ).toString(); } }
StringBridge
personnalisée », toute propriété ou champ peut utiliser ce pont à l'aide de l'annotation @FieldBridge
:
@FieldBridge(impl = PaddedIntegerBridge.class)
private Integer length;
13.2.4.2.2. Pont paramétré
ParameterizedBridge
et les paramètres sont transmis par l'annotation @FieldBridge
.
Exemple 13.26. Transmettre des paramètres vers votre pont mis en œuvre
public class PaddedIntegerBridge implements StringBridge, ParameterizedBridge { public static String PADDING_PROPERTY = "padding"; private int padding = 5; //default public void setParameterValues(Map<String,String> parameters) { String padding = parameters.get( PADDING_PROPERTY ); if (padding != null) this.padding = Integer.parseInt( padding ); } public String objectToString(Object object) { String rawInteger = ( (Integer) object ).toString(); if (rawInteger.length() > padding) throw new IllegalArgumentException( "Try to pad on a number too big" ); StringBuilder paddedInteger = new StringBuilder( ); for ( int padIndex = rawInteger.length() ; padIndex < padding ; padIndex++ ) { paddedInteger.append('0'); } return paddedInteger.append( rawInteger ).toString(); } } //property @FieldBridge(impl = PaddedIntegerBridge.class, params = @Parameter(name="padding", value="10") ) private Integer length;
ParameterizedBridge
peut être mise en œuvre par StringBridge
, TwoWayStringBridge
et FieldBridge
.
13.2.4.2.3. Connaissance du type de pont
- le type de retour de la propriété pour les ponts champ/niveau getter
- le type de classe pour les ponts de niveau classe
AppliedOnTypeAwareBridge
verront le type sur lequel le pont est appliqué se faire injecter. Comme pour les paramètres, il n'est pas nécessaire d'accorder trop d'importance à la sécurité de thread du type injecté.
13.2.4.2.4. Pont bidirectionnel
@DocumentId
), vous devrez utiliser une version légèrement étendue de StringBridge
appelée TwoWayStringBridge
. Hibernate Search doit lire la représentation de chaîne de l'identifiant et en générer l'objet. Il n'y a aucune différence dans la façon d'utiliser l'annotation @FieldBridge
.
Exemple 13.27. Implémentation d'un TwoWayStringBridge (pont bidirectionnel) utilisable avec les propriétés d'ID
public class PaddedIntegerBridge implements TwoWayStringBridge, ParameterizedBridge {
public static String PADDING_PROPERTY = "padding";
private int padding = 5; //default
public void setParameterValues(Map parameters) {
Object padding = parameters.get( PADDING_PROPERTY );
if (padding != null) this.padding = (Integer) padding;
}
public String objectToString(Object object) {
String rawInteger = ( (Integer) object ).toString();
if (rawInteger.length() > padding)
throw new IllegalArgumentException( "Try to pad on a number too big" );
StringBuilder paddedInteger = new StringBuilder( );
for ( int padIndex = rawInteger.length() ; padIndex < padding ; padIndex++ ) {
paddedInteger.append('0');
}
return paddedInteger.append( rawInteger ).toString();
}
public Object stringToObject(String stringValue) {
return new Integer(stringValue);
}
}
//id property
@DocumentId
@FieldBridge(impl = PaddedIntegerBridge.class,
params = @Parameter(name="padding", value="10")
private Integer id;
Important
13.2.4.2.5. FieldBridge
FieldBridge
. Cette interface vous donne une valeur de propriété et vous laisse la mapper à votre manière dans votre Document
Lucene. Vous pouvez par exemple stocker une propriété en deux champs de document différents. L'interface est très similaire dans son concept aux UserType
s de Hibernate.
Exemple 13.28. Mettre en œuvre l'interface FieldBridge
/** * Store the date in 3 different fields - year, month, day - to ease Range Query per * year, month or day (eg get all the elements of December for the last 5 years). * @author Emmanuel Bernard */ public class DateSplitBridge implements FieldBridge { private final static TimeZone GMT = TimeZone.getTimeZone("GMT"); public void set(String name, Object value, Document document, LuceneOptions luceneOptions) { Date date = (Date) value; Calendar cal = GregorianCalendar.getInstance(GMT); cal.setTime(date); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH) + 1; int day = cal.get(Calendar.DAY_OF_MONTH); // set year luceneOptions.addFieldToDocument( name + ".year", String.valueOf( year ), document ); // set month and pad it if needed luceneOptions.addFieldToDocument( name + ".month", month < 10 ? "0" : "" + String.valueOf( month ), document ); // set day and pad it if needed luceneOptions.addFieldToDocument( name + ".day", day < 10 ? "0" : "" + String.valueOf( day ), document ); } } //property @FieldBridge(impl = DateSplitBridge.class) private Date date;
LuceneOptions
; cet assistant appliquera les options que vous avez sélectionnées sur @Field
, comme Store
ou TermVector
, ou appliquera la valeur @Boost
choisie, ce qui est particulièrement utile pour encapsuler la complexité des implémentations COMPRESS
. Bien qu'il soit recommandé de déléguer à LuceneOptions
l'ajout des champs au Document
, rien ne vous empêche de modifier le Document
directement et d'ignorer LuceneOptions
le cas échéant.
Note
LuceneOptions
sont créées pour protéger votre application des changements survenus dans l'API Lucene et pour simplifier votre code. Utilisez-les si vous voulez, sauf si vous souhaitez davantage de flexibilité.
13.2.4.2.6. ClassBridge
@ClassBridge
et les annotations @ClassBridges
respectives peuvent être définis au niveau de la classe (et non pas au niveau propriété). Dans ce cas, l'implémentation du pont reçoit l'instance d'entité comme valeur de paramètre au lieu d'une valeur de propriété en particulier. Bien que cela ne soit pas montré dans Exemple 13.29, « Implémentation d'un pont de classe », @ClassBridge
supporte l'attribut termVector
discuté dans la section Section 13.2.1.1, « Mappage de base ».
Exemple 13.29. Implémentation d'un pont de classe
@Entity @Indexed @ClassBridge(name="branchnetwork", store=Store.YES, impl = CatFieldsClassBridge.class, params = @Parameter( name="sepChar", value=" " ) ) public class Department { private int id; private String network; private String branchHead; private String branch; private Integer maxEmployees ... } public class CatFieldsClassBridge implements FieldBridge, ParameterizedBridge { private String sepChar; public void setParameterValues(Map parameters) { this.sepChar = (String) parameters.get( "sepChar" ); } public void set( String name, Object value, Document document, LuceneOptions luceneOptions) { // In this particular class the name of the new field was passed // from the name field of the ClassBridge Annotation. This is not // a requirement. It just works that way in this instance. The // actual name could be supplied by hard coding it below. Department dep = (Department) value; String fieldValue1 = dep.getBranch(); if ( fieldValue1 == null ) { fieldValue1 = ""; } String fieldValue2 = dep.getNetwork(); if ( fieldValue2 == null ) { fieldValue2 = ""; } String fieldValue = fieldValue1 + sepChar + fieldValue2; Field field = new Field( name, fieldValue, luceneOptions.getStore(), luceneOptions.getIndex(), luceneOptions.getTermVector() ); field.setBoost( luceneOptions.getBoost() ); document.add( field ); } }
CatFieldsClassBridge
particulière est appliquée à l'instance department
, le pont de champ concatène ensuite la branche et le réseau et indexe la concaténation.
13.3. Recherche
- La création d'une
FullTextSession
- La création d'une requête Lucene à l'aide de la DSL de requête Hibernate Search (recommandé) ou l'API de requête Lucene
- L'inclusion d'une requête Lucene avec
org.hibernate.Query
- L'exécution de la recherche en appelant par exemple
list()
ouscroll()
FullTextSession
. Cette session spécifique à la recherche inclut une classe org.hibernate.Session
régulière afin de fournir une requête et des capacités d'indexation.
Exemple 13.30. Création d'une FullTextSession
Session session = sessionFactory.openSession(); ... FullTextSession fullTextSession = Search.getFullTextSession(session);
FullTextSession
pour construire une requête en texte intégral en utilisant la DSL de requête Hibernate Search ou la requête Lucene native.
final QueryBuilder b = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity( Myth.class ).get(); org.apache.lucene.search.Query luceneQuery = b.keyword() .onField("history").boostedTo(3) .matching("storm") .createQuery(); org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery ); List result = fullTextQuery.list(); //return a list of managed objects
Exemple 13.31. Création d'une requête Lucene à l'aide de QueryParser
SearchFactory searchFactory = fullTextSession.getSearchFactory(); org.apache.lucene.queryParser.QueryParser parser = new QueryParser("title", searchFactory.getAnalyzer(Myth.class) ); try { org.apache.lucene.search.Query luceneQuery = parser.parse( "history:storm^3" ); } catch (ParseException e) { //handle parsing failure } org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery); List result = fullTextQuery.list(); //return a list of managed objects
org.hibernate.Query
. Cette requête demeure dans le même paradigme que d'autres fonctions de requête Hibernate, telles que HQL (Hibernate Query Language), Native et Criteria. Utilisez des méthodes telles que list()
, uniqueResult()
, iterate()
et scroll()
avec la requête.
Exemple 13.32. Création d'une requête de recherche à l'aide de l'API JPA
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager =
org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
...
final QueryBuilder b = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity( Myth.class ).get();
org.apache.lucene.search.Query luceneQuery =
b.keyword()
.onField("history").boostedTo(3)
.matching("storm")
.createQuery();
javax.persistence.Query fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery );
List result = fullTextQuery.getResultList(); //return a list of managed objects
Note
FullTextQuery
est extraite.
13.3.1. Création de requêtes
13.3.1.1. Générer une requête Lucene avec l'API Lucene
13.3.1.2. Génération d'une requête Lucene
QueryBuilder
pour cette tâche.
- Les noms de méthodes sont en anglais. Ainsi, les opérations API peuvent être lues et comprises comme une série d'expressions et instructions en anglais.
- Elle utilise le complètement automatique IDE ce qui facilite les complètements pour le préfixe d'entrée actuel et permet à l'utilisateur de choisir l'option qui convient.
- Elle utilise souvent le modèle de la méthode de chaîne.
- Les opérations API sont faciles à lire et à utiliser.
indexedentitytype
donné. Ce QueryBuilder
sait quel analyseur utiliser et quel pont de champ appliquer. Plusieurs QueryBuilder
(un pour chaque type d'entité impliqué dans la racine de votre requête) peuvent être créés. Le QueryBuilder
provient de SearchFactory
.
QueryBuilder mythQB = searchFactory.buildQueryBuilder().forEntity( Myth.class ).get();
QueryBuilder mythQB = searchFactory.buildQueryBuilder() .forEntity( Myth.class ) .overridesForField("history","stem_analyzer_definition") .get();
Query
assemblés avec l'API programmée de Lucene sont utilisées avec Hibernate Search DSL.
13.3.1.3. Requêtes de mots-clés
Query luceneQuery = mythQB.keyword().onField("history").matching("storm").createQuery();
Tableau 13.4. Paramètres de requête de mot-clé
Paramètre | Description |
---|---|
keyword() | Utilisez ce paramètre pour trouver un mot spécifique |
onField() | Utilisez ce paramètre pour spécifier le champ Lucene dans lequel rechercher le mot |
matching() | Utilisez ce paramètre pour spécifier la correspondance pour chaque chaîne |
createQuery() | Crée l'objet de requête de Lucene |
- La valeur « storm » est transmise à la classe
history
FieldBridge
, ce qui est utile lorsque les nombres ou dates sont impliqués. - La valeur de pont du champ est ensuite transmise à l'analyseur utilisé pour indexer le champ
history
, ce qui permet de garantir que la requête utilise la même transformation de terme que l'indexation (bas de casse, ngram, analyse de radical etc.). Si le processus d'analyse génère plusieurs termes pour un même mot, une requête booléenne sera utilisée avec la logiqueSHOULD
(semblable à une logiqueOR
).
@Indexed public class Myth { @Field(analyze = Analyze.NO) @DateBridge(resolution = Resolution.YEAR) public Date getCreationDate() { return creationDate; } public Date setCreationDate(Date creationDate) { this.creationDate = creationDate; } private Date creationDate; ... } Date birthdate = ...; Query luceneQuery = mythQb.keyword().onField("creationDate").matching(birthdate).createQuery();
Note
Date
devait être converti en sa représentation de chaîne (dans ce cas-là, l'année)
FieldBridge
ait une méthode objectToString
(ce qui est le cas pour toutes les mises en œuvre FieldBridge
intégrées).
@AnalyzerDef(name = "ngram", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class ), filters = { @TokenFilterDef(factory = StandardFilterFactory.class), @TokenFilterDef(factory = LowerCaseFilterFactory.class), @TokenFilterDef(factory = StopFilterFactory.class), @TokenFilterDef(factory = NGramFilterFactory.class, params = { @Parameter(name = "minGramSize", value = "3"), @Parameter(name = "maxGramSize", value = "3") } ) } ) public class Myth { @Field(analyzer=@Analyzer(definition="ngram") public String getName() { return name; } public String setName(String name) { this.name = name; } private String name; ... } Date birthdate = ...; Query luceneQuery = mythQb.keyword().onField("name").matching("Sisiphus") .createQuery();
y
). Tout cela est fait de manière transparente pour l'utilisateur.
Note
ignoreAnalyzer()
ou ignoreFieldBridge()
peuvent être appelées.
//search document with storm or lightning in their history Query luceneQuery = mythQB.keyword().onField("history").matching("storm lightning").createQuery();
onFields
.
Query luceneQuery = mythQB .keyword() .onFields("history","description","name") .matching("storm") .createQuery();
andField()
.
Query luceneQuery = mythQB.keyword() .onField("history") .andField("name") .boostedTo(5) .andField("description") .matching("storm") .createQuery();
13.3.1.4. Requêtes Fuzzy (approximatives)
keyword
et ajoutez l'indicateur fuzzy
.
Query luceneQuery = mythQB .keyword() .fuzzy() .withThreshold( .8f ) .withPrefixLength( 1 ) .onField("history") .matching("starm") .createQuery();
threshold
est la limite au-dessus de laquelle deux termes sont considérés comme correspondant. Il s'agit d'une décimal entre 0 et 1 et la valeur par défaut est 0.5. Le prefixLength
est la longueur du préfix ignoré par l'« approximation ». Bien que la valeur par défaut soit 0, une valeur autre que 0 est recommandée pour les indexes contenant un grand nombre de termes distincts.
13.3.1.5. Requêtes Wildcard
?
représente un caractère unique et *
représente plusieurs caractères. Veuillez noter que dans un soucis de performance, il est recommandé que la requête ne commence pas par ?
ou *
.
Query luceneQuery = mythQB .keyword() .wildcard() .onField("history") .matching("sto*") .createQuery();
Note
*
ou ?
altéré est trop grand.
13.3.1.6. Requêtes de phrase
phrase()
.
Query luceneQuery = mythQB .phrase() .onField("history") .sentence("Thou shalt not kill") .createQuery();
Query luceneQuery = mythQB .phrase() .withSlop(3) .onField("history") .sentence("Thou kill") .createQuery();
13.3.1.7. Requêtes de portée
//look for 0 <= starred < 3 Query luceneQuery = mythQB .range() .onField("starred") .from(0).to(3).excludeLimit() .createQuery(); //look for myths strictly BC Date beforeChrist = ...; Query luceneQuery = mythQB .range() .onField("creationDate") .below(beforeChrist).excludeLimit() .createQuery();
13.3.1.8. Association de requêtes
SHOULD
(devrait) : la requête devrait contenir les éléments de correspondance de la sous-requête.MUST
(doit) : la requête doit contenir les éléments correspondants de la sous-requête.MUST NOT
(ne doit pas) : la requête ne doit pas contenir les éléments correspondants de la sous-requête.
Exemple 13.33. Requête MUST NOT
//look for popular modern myths that are not urban Date twentiethCentury = ...; Query luceneQuery = mythQB .bool() .must( mythQB.keyword().onField("description").matching("urban").createQuery() ) .not() .must( mythQB.range().onField("starred").above(4).createQuery() ) .must( mythQB .range() .onField("creationDate") .above(twentiethCentury) .createQuery() ) .createQuery();
Exemple 13.34. Requête SHOULD
//look for popular myths that are preferably urban Query luceneQuery = mythQB .bool() .should( mythQB.keyword().onField("description").matching("urban").createQuery() ) .must( mythQB.range().onField("starred").above(4).createQuery() ) .createQuery();
Exemple 13.35. Requête NOT
//look for all myths except religious ones Query luceneQuery = mythQB .all() .except( monthQb .keyword() .onField( "description_stem" ) .matching( "religion" ) .createQuery() ) .createQuery();
13.3.1.9. Options de requête
- La méthode
boostedTo
(sur les champs et types de requêtes) booste la requête entière ou le champ spécifique vers un facteur donné. - La méthode
withConstantScore
(sur requête) renvoie tous les résultats correspondants à la requête ayant un score égal au boost. - La méthode
filteredBy(Filter)
(sur requête) filtre les résultats de requêtes à l'aide de l'instanceFilter
. - La méthode
ignoreAnalyzer
(sur champ) ignore l'analyseur lors du traitement de ce champ. - La méthode
ignoreFieldBridge
(sur champ) ignore le pont de champ lors du traitement de ce champ.
Exemple 13.36. Associations d'options de requêtes
Query luceneQuery = mythQB .bool() .should( mythQB.keyword().onField("description").matching("urban").createQuery() ) .should( mythQB .keyword() .onField("name") .boostedTo(3) .ignoreAnalyzer() .matching("urban").createQuery() ) .must( mythQB .range() .boostedTo(5).withConstantScore() .onField("starred").above(4).createQuery() ) .createQuery();
13.3.1.10. Créer une requête Hibernate Search
13.3.1.10.1. Généralités
Exemple 13.37. Inclure une requête Lucene dans une requête Hibernate.
FullTextSession fullTextSession = Search.getFullTextSession( session ); org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
Exemple 13.38. Filtrage des résultats de la recherche par type d'entité
fullTextQuery = fullTextSession .createFullTextQuery( luceneQuery, Customer.class ); // or fullTextQuery = fullTextSession .createFullTextQuery( luceneQuery, Item.class, Actor.class );
Customer
correspondants. La seconde partie du même exemple renvoie des Actor
et Item
correspondants. La restriction de type est polymorphe. Ainsi, si les deux sous-classes Salesman
et Customer
de la classe de base Person
apparaissent dans les résultats, spécifiez Person.class
pour filtre sur la base des types de résultat.
13.3.1.10.2. Pagination
Exemple 13.39. Définir une pagination pour une requête de recherche
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class ); fullTextQuery.setFirstResult(15); //start from the 15th element fullTextQuery.setMaxResults(10); //return 10 elements
Note
fulltextQuery.
getResultSize()
13.3.1.10.3. Tri
Exemple 13.40. Spécifier un Sort
Lucene
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
org.apache.lucene.search.Sort sort = new Sort(
new SortField("title", SortField.STRING));
query.setSort(sort);
List results = query.list();
Note
13.3.1.10.4. Stratégie de récupération
Exemple 13.41. Spécification de FetchMode
dans une requête
Criteria criteria = s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN ); s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );
Important
Criteria
car la méthode getResultSize()
émet une SearchException
lorsqu'elle est utilisée en association avec une classe Criteria
avec restriction.
setCriteriaQuery
.
13.3.1.10.5. Projection
Object[]
. Les projections permettent d'éviter les longs allers-retours dans la base de données, mais elles présentent également les contraintes suivantes :
- Les propriétés projetées doivent être stockées dans l'index (
@Field(store=Store.YES)
), ce qui augmente la taille de l'index. - Les propriétés projetées doivent utiliser un
FieldBridge
mettant en œuvreorg.hibernate.search.bridge.TwoWayFieldBridge
ouorg.hibernate.search.bridge.TwoWayStringBridge
, ce dernier étant la version la plus simple.Note
Tous les types d'Hibernate Search intégrés sont bidirectionnels. - Seules les propriétés simples de l'entité indexée ou de ses associations intégrées peuvent être projetées : une entité entière intégrée ne peut donc pas être projetée.
- La projection ne fonctionne pas sur les collections ou cartes indexées par le biais de
@IndexedEmbedded
Exemple 13.42. Utilisation de la projection pour récupérer les métadonnées
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( FullTextQuery.SCORE, FullTextQuery.THIS, "mainAuthor.name" );
List results = query.list();
Object[] firstResult = (Object[]) results.get(0);
float score = firstResult[0];
Book book = firstResult[1];
String authorName = firstResult[2];
FullTextQuery.THIS
: renvoie l'entité gérée et initialisée (tel que l'aurait fait une requête non projetée).FullTextQuery.DOCUMENT
: renvoie le document Lucene lié à l'objet projeté.FullTextQuery.OBJECT_CLASS
: renvoie la classe de l'entité indexée.FullTextQuery.SCORE
: renvoie le score du document dans la requête. Les scores sont utiles pour comparer deux résultats d'une requête donnée, mais sont inutiles pour comparer le résultat de différentes requêtes.FullTextQuery.ID
: la valeur de propriété de l'ID de l'objet projeté.FullTextQuery.DOCUMENT_ID
: l'ID du document Lucene. Utilisez cette valeur avec précaution car un ID de document Lucene peut changer entre deux ouvertures IndexReader différentes.FullTextQuery.EXPLANATION
: renvoie l'objet d'explication Lucene pour l'objet/document correspondant dans une requête donnée mais ne convient pas à la récupération de grandes quantités de données. L'exécution d'explication coûte autant que d'exécuter la requête Lucene entière par élément correspondant. La projection est donc recommandée.
13.3.1.10.6. Personnalisation de stratégies d'initialisation d'objet
Exemple 13.43. Vérifiez le cache de secon niveau avant d'utiliser une requête
FullTextQuery query = session.createFullTextQuery(luceneQuery, User.class); query.initializeObjectWith( ObjectLookupMethod.SECOND_LEVEL_CACHE, DatabaseRetrievalMethod.QUERY );
ObjectLookupMethod
définit la stratégie consistant à vérifier si un objet est facilement accessible (sans avoir à le trouver dans une base de données). Autres options :
ObjectLookupMethod.PERSISTENCE_CONTEXT
est utilisé si un grand nombre d'entités correspondantes sont déjà chargées dans le contexte de persistance (chargées dansSession
ouEntityManager
).ObjectLookupMethod.SECOND_LEVEL_CACHE
vérifie le contexte de persistance puis le cache de second niveau.
- Configurez et activez correctement le cache de second niveau.
- Activez le cache de second niveau pour l'entité souhaitée à l'aide d'annotations telles que
@Cacheable
. - Activez l'accès de lecture de cache de second niveau pour
Session
,EntityManager
ouQuery
. UtilisezCacheMode.NORMAL
dans les API natives de Hibernate ouCacheRetrieveMode.USE
dans les API de Persistance Java.
Avertissement
ObjectLookupMethod.SECOND_LEVEL_CACHE
. D'autres fournisseurs de cache de second niveau ne mettent pas en œuvre cette opération de manière efficace.
DatabaseRetrievalMethod
comme suit :
QUERY
(par défaut) utilise un ensemble de requêtes pour charger différents objets dans chaque lot. Cette approche est recommandée.FIND_BY_ID
charge un objet à la fois à l'aide de la sémantiqueSession
.get
ouEntityManager
.find
. Cette approche est recommandée si la taille du lot est définie pour l'entité, ce qui permet à Hibernate Core de charger des entités par lots.
13.3.1.10.7. Limiter le temps d'une requête
- Lever une exception lorsque la limite est atteinte.
- Limiter au nombre de résultats récupérés lorsque la limite de temps est levée
13.3.1.10.8. Lever une Exception de Limite de temps
QueryTimeoutException
est levée (org.hibernate.QueryTimeoutException
ou javax.persistence.QueryTimeoutException
suivant l'API programmatique).
Exemple 13.44. Définition d'un Timeout dans une exécution de requête
Query luceneQuery = ...; FullTextQuery query = fullTextSession.createFullTextQuery(luceneQuery, User.class); //define the timeout in seconds query.setTimeout(5); //alternatively, define the timeout in any given time unit query.setTimeout(450, TimeUnit.MILLISECONDS); try { query.list(); } catch (org.hibernate.QueryTimeoutException e) { //do something, too slow }
getResultSize()
, iterate()
et scroll()
respectent le timeout jusqu'à la fin de la méthode. C'est pourquoi Iterable
ou ScrollableResults
ignorent le timeout. De plus, la méthode explain()
ne respecte pas cette période de timeout. Cette méthode est utilisée pour déboguer et pour vérifier les raisons derrière la contre-performance d'une requête.
Exemple 13.45. Définition d'un Timeout dans une exécution de requête
Query luceneQuery = ...; FullTextQuery query = fullTextEM.createFullTextQuery(luceneQuery, User.class); //define the timeout in milliseconds query.setHint( "javax.persistence.query.timeout", 450 ); try { query.getResultList(); } catch (javax.persistence.QueryTimeoutException e) { //do something, too slow }
Important
13.3.2. Récupération des résultats
list()
, uniqueResult()
, iterate()
et scroll()
sont disponibles.
13.3.2.1. Performance
list()
ou uniqueResult()
sont recommandés. list()
fonctionne mieux si l'entité batch-size
est configurée correctement. Veuillez noter que Hibernate Search doit traiter tous les éléments Lucene Hits (dans la pagination) lors de l'utilisation de list()
, uniqueResult()
et iterate()
.
scroll()
. Veillez à fermer l'objet ScrollableResults
lorsque vous aurez terminé, puisqu'il garde les ressources Lucene. Si vous comptez utiliser la méthode scroll,
mais souhaitez charger des objets en lot, vous pouvez utiliser query.setFetchSize()
. Lorsqu'on accède à un objet mais qu'il n'est pas encore chargé, Hibernate Search charge les objets qui suivent fetchSize
en une seule fois.
Important
13.3.2.2. Taille des résultats
- pour la fonctionnalité de type Google "1-10 sur environ 888 000 000"
- pour implémenter une navigation de pagination rapide ;
- pour mettre en place un moteur de recherche à plusieurs étapes (ajout d'une approximation si la requête restreinte renvoie peu ou pas de résultats) ;
- pour fournir une fonctionnalité de résultat total de la recherche, comme pour une recherche Google. Par exemple, « 1-10 sur environ 888 000 000 résultats » ;
- pour mettre en place une navigation de pagination rapide ;
- pour mettre en place un moteur de recherche à plusieurs étapes qui ajoute une approximation si la requête restreinte renvoie peu ou pas de résultats ;
Exemple 13.46. Détermination de la taille des résultats d'une requête
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class ); //return the number of matching books without loading a single one assert 3245 == query.getResultSize(); org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class ); query.setMaxResult(10); List results = query.list(); //return the total number of matching books regardless of pagination assert 3245 == query.getResultSize();
Note
13.3.2.3. ResultTransformer
Object
. Si la structure des données utilisée pour l'objet ne correspond pas aux pré-requis de l'application, appliquez une classe ResultTransformer
. La classe ResultTransformer
génère la structure de données requise après l'exécution de la requête.
Exemple 13.47. Utilisation de ResultTransformer avec projections
org.hibernate.search.FullTextQuery query =
s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );
query.setResultTransformer( new StaticAliasToBeanResultTransformer( BookView.class, "title", "author" ) );
List<BookView> results = (List<BookView>) query.list();
for(BookView view : results) {
log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
ResultTransformer
se trouvent dans la base de codes Hibernate Core.
13.3.2.4. Interprétation des résultats
Luke
vous aidera à les déchiffrer. Cependant, Hibernate Search vous donnera également accès à l'objet Explanation
Lucene (dans une requête donnée). Cette classe peut sembler complexe pour certains utilisateurs Lucene mais elle aide à la compréhension de la notation d'un objet. Vous pouvez accéder à l'objet Explanation de deux manières :
- Utiliser la méthode
fullTextQuery.explain(int)
- Utiliser la projection
FullTextQuery.DOCUMENT_ID
.
Avertissement
Explanation
à l'aide de la constante FullTextQuery.EXPLANATION
.
Exemple 13.48. Récupération de l'objet Explanation de Lucene à l'aide d'une projection
FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
.setProjection(
FullTextQuery.DOCUMENT_ID,
FullTextQuery.EXPLANATION,
FullTextQuery.THIS );
@SuppressWarnings("unchecked") List<Object[]> results = ftQuery.list();
for (Object[] result : results) {
Explanation e = (Explanation) result[1];
display( e.toString() );
}
13.3.3. Filters
- sécurité
- données temporelles (par exemple, afficher uniquement les données du mois précédent)
- filtre de population (par exemple, recherche limitée à une catégorie donnée)
13.3.3.1. Utilisation des filtres dans un environnement partitionné
Procédure 13.1. Requête de sous-ensemble de partitions d'index
- Créer une stratégie de partitionnement qui sélectionne un sous-ensemble de
IndexManager
s selon la configuration du filtre. - Activer le filtre au moment de la requête.
Exemple 13.49. Requête de sous-ensemble de partitions d'index
customer
(client) est activé.
public class CustomerShardingStrategy implements IndexShardingStrategy { // stored IndexManagers in a array indexed by customerID private IndexManager[] indexManagers; public void initialize(Properties properties, IndexManager[] indexManagers) { this.indexManagers = indexManagers; } public IndexManager[] getIndexManagersForAllShards() { return indexManagers; } public IndexManager getIndexManagerForAddition( Class<?> entity, Serializable id, String idInString, Document document) { Integer customerID = Integer.parseInt(document.getFieldable("customerID").stringValue()); return indexManagers[customerID]; } public IndexManager[] getIndexManagersForDeletion( Class<?> entity, Serializable id, String idInString) { return getIndexManagersForAllShards(); } /** * Optimization; don't search ALL shards and union the results; in this case, we * can be certain that all the data for a particular customer Filter is in a single * shard; simply return that shard by customerID. */ public IndexManager[] getIndexManagersForQuery( FullTextFilterImplementor[] filters) { FullTextFilter filter = getCustomerFilter(filters, "customer"); if (filter == null) { return getIndexManagersForAllShards(); } else { return new IndexManager[] { indexManagers[Integer.parseInt( filter.getParameter("customerID").toString())] }; } } private FullTextFilter getCustomerFilter(FullTextFilterImplementor[] filters, String name) { for (FullTextFilterImplementor filter: filters) { if (filter.getName().equals(name)) return filter; } return null; } }
customer
est présent, seule la partition consacrée à ce client sera requise, sinon, toutes les partitions seront retournées. Une stratégie de partitionnement donnée peut réagir à un ou plusieurs filtres et dépendra de ces paramètres.
ShardSensitiveOnlyFilter
lorsque vous indiquez votre filtre.
@Indexed @FullTextFilterDef(name="customer", impl=ShardSensitiveOnlyFilter.class) public class Customer { ... } FullTextQuery query = ftEm.createFullTextQuery(luceneQuery, Customer.class); query.enableFulltextFilter("customer").setParameter("CustomerID", 5); @SuppressWarnings("unchecked") List<Customer> results = query.getResultList();
ShardSensitiveOnlyFilter
, vous n'aurez pas besoin de mettre en place un filtre Lucene. L'utilisation de filtres et d'une stratégie de partitionnement réagissant à ces filtres est recommandée pour accélérer les requêtes dans un environnement partitionné.
13.3.4. Requêtes de facette
Exemple 13.50. Recherche Hibernate Search sur Amazon
QueryBuilder
et FullTextQuery
sont le point d'entrée dans l'API de facette. Le premier crée des requêtes de facettes et le dernier accède à la classe FacetManager
. Le FacetManager
applique des requêtes de facette sur une requête et sélectionne des facettes ajoutées à une requête existante pour affiner les résultats de la recherche. Les exemples utilisent l'entité Cd
tel que démontré dans Exemple 13.51, « Entité Cd »:
Figure 13.1. Recherche Hibernate Search sur Amazon
Exemple 13.51. Entité Cd
@Indexed public class Cd { private int id; @Fields( { @Field, @Field(name = "name_un_analyzed", analyze = Analyze.NO) }) private String name; @Field(analyze = Analyze.NO) @NumericField private int price; Field(analyze = Analyze.NO) @DateBridge(resolution = Resolution.YEAR) private Date releaseYear; @Field(analyze = Analyze.NO) private String label; // setter/getter ...
13.3.4.1. Création d'une requête de facette
FacetingRequest
. Deux types de requête de facette sont pour l'instant pris en charge. Le premier type s'intitule « discrete faceting » (requête de facette discrète) et le second « range faceting » (requête de facette de gamme). Dans le cas d'une requête de facette discrète, vous devez spécifier le champ d'index sur lequel vous souhaitez effectuer la requête de facette (catégoriser) et les options de requête de facette à appliquer. Vous trouverez un exemple de requête de facette discrète dans Exemple 13.52, « Création d'une requête de facette discrète »:
Exemple 13.52. Création d'une requête de facette discrète
QueryBuilder builder = fullTextSession.getSearchFactory() .buildQueryBuilder() .forEntity( Cd.class ) .get(); FacetingRequest labelFacetingRequest = builder.facet() .name( "labelFaceting" ) .onField( "label") .discrete() .orderedBy( FacetSortOrder.COUNT_DESC ) .includeZeroCounts( false ) .maxFacetCount( 1 ) .createFacetingRequest();
Facet
sera créée pour chaque valeur discrète pour le champ indexé label
. L'instance Facet
enregistrera la valeur de champ réelle en incluant la fréquence à laquelle cette valeur de champ apparaît dans les résultats de requête initiaux. orderedBy
, includeZeroCounts
et maxFacetCount
sont des paramètres facultatifs pouvant être appliqués sur toute requête de facette. orderedBy
permet de spécifier l'ordre dans lequel les facettes créées seront renvoyées. La valeur par défaut est FacetSortOrder.COUNT_DESC
, mais vous pouvez également trier par valeur de champ ou par l'ordre dans lequel les gammes sont spécifiées. La méthode includeZeroCount
détermine si les facettes avec un résultat de 0 seront incluses dans le résultat (elles le seront par défaut) et la méthode maxFacetCount
vous permet de limiter le nombre maximal de facettes renvoyées.
Note
String
, Date
ou un sous-type des valeurs Number
et null
doivent être évitées. De plus, la propriété doit être indexée avec Analyze.NO
et en cas de propriété numérique la classe @NumericField
doit être spécifiée.
below
et above
ne peuvent être spécifiées qu'une fois, mais vous pouvez spécifier autant de from
- to
que vous le souhaitez. Pour chaque limite de gamme, vous pouvez également spécifier avec excludeLimit
si elle est incluse dans la gamme ou non.
Exemple 13.53. Création d'une requête de facette de gamme
QueryBuilder builder = fullTextSession.getSearchFactory() .buildQueryBuilder() .forEntity( Cd.class ) .get(); FacetingRequest priceFacetingRequest = builder.facet() .name( "priceFaceting" ) .onField( "price" ) .range() .below( 1000 ) .from( 1001 ).to( 1500 ) .above( 1500 ).excludeLimit() .createFacetingRequest();
13.3.4.2. Application d'une requête de facette
FacetManager
qui peut être récupérée par la classe FullTextQuery
.
getFacets()
en spécifiant le nom de la requête de facette. Il existe également une méthode disableFaceting()
qui vous permet de désactiver une requête de facette en spécifiant son nom.
Exemple 13.54. Application d'une requête de facette
// create a fulltext query Query luceneQuery = builder.all().createQuery(); // match all query FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Cd.class ); // retrieve facet manager and apply faceting request FacetManager facetManager = fullTextQuery.getFacetManager(); facetManager.enableFaceting( priceFacetingRequest ); // get the list of Cds List<Cd> cds = fullTextQuery.list(); ... // retrieve the faceting results List<Facet> facets = facetManager.getFacets( "priceFaceting" ); ...
13.3.4.3. Restriction des résultats de requête
Facet
s renvoyées en tant que critère supplémentaire sur votre critère d'origine de manière à mettre en œuvre une fonctionnalité "drill-down" (Résultats détaillés en cascade). FacetSelection
peut être utilisé à cette fin. Les FacetSelection
s sont disponibles sur le FacetManager
et vous permettent de sélectionner une facette comme critère de requête (selectFacets
), de supprimer une restriction de facette (deselectFacets
), de supprimer toutes les restrictions de facette (clearSelectedFacets
) et de récupérer toutes les facettes actuellement sélectionnées (getSelectedFacets
). Exemple 13.55, « Restreindre des résultats de requête à l'aide de l'application d'une FacetSelection
» vous montre un exemple.
Exemple 13.55. Restreindre des résultats de requête à l'aide de l'application d'une FacetSelection
// create a fulltext query Query luceneQuery = builder.all().createQuery(); // match all query FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, clazz ); // retrieve facet manager and apply faceting request FacetManager facetManager = fullTextQuery.getFacetManager(); facetManager.enableFaceting( priceFacetingRequest ); // get the list of Cd List<Cd> cds = fullTextQuery.list(); assertTrue(cds.size() == 10); // retrieve the faceting results List<Facet> facets = facetManager.getFacets( "priceFaceting" ); assertTrue(facets.get(0).getCount() == 2) // apply first facet as additional search criteria facetManager.getFacetGroup( "priceFaceting" ).selectFacets( facets.get( 0 ) ); // re-execute the query cds = fullTextQuery.list(); assertTrue(cds.size() == 2);
13.3.5. Optimisation des procédés de requête
- La requête Lucene.
- Le nombre d'objets chargés : toujours utiliser la pagination ou la projection d'index (le cas échéant).
- La façon dont Hibernate Search intéragit avec les lecteurs Lucene : définit la stratégie de lecteur appropriée.
- Mise en cache des valeurs extraites fréquemment à partir de l'index : cf. Section 13.3.5.1, « Mise en cache des valeurs d'index : cache du champ »
13.3.5.1. Mise en cache des valeurs d'index : cache du champ
CacheFromIndex
vous permet de tester différents types de mise en cache des champs de métadonnées principales requis par Hibernate Search :
import static org.hibernate.search.annotations.FieldCacheType.CLASS; import static org.hibernate.search.annotations.FieldCacheType.ID; @Indexed @CacheFromIndex( { CLASS, ID } ) public class Essay { ...
CLASS
: Hibernate Search utilise un cache de champ Lucene pour améliorer la performance de l'extraction du type de Classe à partir de l'index.Cette valeur est activée par défaut et sera appliquée par Hibernate Search si vous ne précisez pas l'annotation @CacheFromIndex
.ID
: l'extraction de l'identifiant principal utilisera une mise en cache, ce qui permet l'obtention de meilleurs résultats, mais signifie également que davantage de mémoire sera utilisée et que la performance en sera affectée.
Note
- L'utilisation de la mémoire : ces caches sont de grands consommateurs de mémoire. Le cache CLASS a généralement des besoins moins importants que le cache ID.
- Premières utilisations de l'Index : à l'utilisation des Field Caches, la première requête sur un nouvel index ou segment sera plus lente qu'en l'absence de cache.
CLASS
, il ne sera pas utilisé ; par exemple, si vous ciblez une classe simple, tous les résultats seront de ce type (l'évaluation sera exécutée à chaque requête).
TwoWayFieldBridge
et chaque type chargé dans une requête spécifique doit utiliser le nom de champ de l'ID et avoir des ID du même type (ceci est évalué à chaque exécution de requête).
13.4. Modifications d'index manuelles
13.4.1. Ajout d'instances à l'index
FullTextSession
.index(T entity)
vous permet d'ajouter ou de mettre à jour directement une instance d'objet spécifique à l'index. Si cette entité a été déjà été indexée, l'index sera alors mis à jour. Les changements apportés à l'index ne sont appliqués à la validation de la transaction.
Exemple 13.56. Indexation d'une entité avec FullTextSession.index(T entity)
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
Object customer = fullTextSession.load( Customer.class, 8 );
fullTextSession.index(customer);
tx.commit(); //index only updated at commit time
MassIndexer
: voir Section 13.4.3.2, « Utilisation d'un MassIndexer » pour plus d'informations.
13.4.2. Supprimer des instances d'un index
FullTextSession
.
Exemple 13.57. Nettoyage d'une instance spécifique d'une entité à partir de l'index
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
for (Customer customer : customers) {
fullTextSession.purge( Customer.class, customer.getId() );
}
tx.commit(); //index is updated at commit time
purgeAll
. Cette opération supprime toutes les entités du type transmis comme paramètre ainsi que tous ses sous-types.
Exemple 13.58. Nettoyage de toutes les instances spécifiques d'une entité à partir de l'index
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();
fullTextSession.purgeAll( Customer.class );
//optionally optimize the index
//fullTextSession.getSearchFactory().optimize( Customer.class );
tx.commit(); //index changes are applied at commit time
Note
index
, purge
et purgeAll
sont également disponibles sur FullTextEntityManager
.
Note
index
, purge
et purgeAll
) affectent uniquement l'index, pas la base de données. Elles sont cependant transactionnelles et ne seront donc appliquées qu'une fois la transaction validée ou si vous utilisez la méthode flushToIndexes
.
13.4.3. Reconstruction de l'index
- Utiliser
FullTextSession
.flushToIndexes()
de manière périodique tout en utilisantFullTextSession
.index()
sur d'autres entités. - Utiliser un
MassIndexer
.
13.4.3.1. Utilisation de flushToIndexes()
FullTextSession
.purgeAll()
et FullTextSession
.index()
. Il existe cependant quelques contraintes de mémoire et d'efficacité. Pour garantir une plus grande efficacité, Hibernate Search regroupe les opérations d'index et les exécute au moment de la validation. Si un grand nombre de données doit être indexé, veuiller faire attention à la consommation de la mémoire puisque tous les documents seront placés dans une file d'attente jusqu'à la validation de la transaction. Une erreur intitulée OutOfMemoryException
peut apparaître si vous ne videz pas la file d'attente régulièrement, ce que vous pouvez faire à l'aide de fullTextSession.flushToIndexes()
. À chaque application de la méthode fullTextSession.flushToIndexes()
(ou lorsque la transaction est validée), la file d'attente est traitée et les modifications d'index sont appliquées. Veuillez noter qu'une fois le vidage effectué, les modifications sont irréversibles.
Exemple 13.59. Reconstruction de l'index à l'aide de index() et flushToIndexes()
fullTextSession.setFlushMode(FlushMode.MANUAL); fullTextSession.setCacheMode(CacheMode.IGNORE); transaction = fullTextSession.beginTransaction(); //Scrollable results will avoid loading too many objects in memory ScrollableResults results = fullTextSession.createCriteria( Email.class ) .setFetchSize(BATCH_SIZE) .scroll( ScrollMode.FORWARD_ONLY ); int index = 0; while( results.next() ) { index++; fullTextSession.index( results.get(0) ); //index each element if (index % BATCH_SIZE == 0) { fullTextSession.flushToIndexes(); //apply changes to indexes fullTextSession.clear(); //free memory since the queue is processed } } transaction.commit();
Note
hibernate.search.default.worker.batch_size
a été remplacé par cette API explicite et offrant un meilleur contrôle
13.4.3.2. Utilisation d'un MassIndexer
MassIndexer
de Hibernate Search utilise plusieurs threads parallèles pour regénérer l'index. Vous pouvez sélectionner les entités devant être rechargées ou faire en sorte que toutes les entités sont réindexées. Cette approche est optimisée pour une meilleure performance mais nécessite de définir l'application en mode maintenance. Envoyer des requêtes à l'index n'est pas recommandé lorsqu'un MassIndexer est occupé.
Exemple 13.60. Regénérer l'index à l'aide de MassIndexer
fullTextSession.createIndexer().startAndWait();
Avertissement
Exemple 13.61. Utilisation d'un MassIndexer réglé.
fullTextSession .createIndexer( User.class ) .batchSizeToLoadObjects( 25 ) .cacheMode( CacheMode.NORMAL ) .threadsToLoadObjects( 12 ) .idFetchSize( 150 ) .progressMonitor( monitor ) //a MassIndexerProgressMonitor implementation .startAndWait();
FieldBridge
personnalisés ou des ClassBridge
pour sortir un document Lucene. Les threads déclenchent un chargement différé d'attributs supplémentaires pendant le processus de conversion. Pour cette raison, un grand nombre de threads parallèles est requis. Le nombre de threads fonctionnant sur l'écriture d'index est défni dans la confiugration de backend de chaque index.
CacheMode.IGNORE
(valeur par défaut), car dans la plupart des situations d'indexation le cache sera une surcharge supplémentaire inutile. Il peut s'avérer utile d'activer un autre CacheMode
selon vos données car il pourrait améliorer la performance si l'entité principale est en rapport avec des données de type énumération incluses dans l'index.
Note
Note
hibernate.search.[default|<indexname>].exclusive_index_use
hibernate.search.[default|<indexname>].indexwriter.max_buffered_docs
hibernate.search.[default|<indexname>].indexwriter.max_merge_docs
hibernate.search.[default|<indexname>].indexwriter.merge_factor
hibernate.search.[default|<indexname>].indexwriter.merge_min_size
hibernate.search.[default|<indexname>].indexwriter.merge_max_size
hibernate.search.[default|<indexname>].indexwriter.merge_max_optimize_size
hibernate.search.[default|<indexname>].indexwriter.merge_calibrate_by_deletes
hibernate.search.[default|<indexname>].indexwriter.ram_buffer_size
hibernate.search.[default|<indexname>].indexwriter.term_index_interval
max_field_length
mais qui a été retiré de Lucene. Il est possible d'obtenir un effet similaire en utilisant LimitTokenCountAnalyzer
.
.indexwriter
sont spécifiques à Lucene et Hibernate Search utilise ces paramètres.
MassIndexer
utilise un résultat déroulant uniquement pour itérer les clés primaires à charger, mais le pilote JDBC de MySQL chargera toutes les valeurs dans la mémoire. Pour éviter cela, cette optimisation définit idFetchSize
à Integer.MIN_VALUE
.
13.5. Optimisation de l'index
- Sur un système inactif ou lorsque les recherches sont les moins fréquentes.
- Après qu'un grand nombre de modifications d'index ont été appliquées.
MassIndexer
(cf. Section 13.4.3.2, « Utilisation d'un MassIndexer ») optimisera les index par défaut au début et à la fin du processus. Vous pouvez changer ce comportement en utilisant respectivement MassIndexer
.optimizeAfterPurge
et MassIndexer
.optimizeOnFinish
.
MassIndexer
(cf. Section 13.4.3.2, « Utilisation d'un MassIndexer ») optimise les index par défaut au début et à la fin du processus. Utilisez MassIndexer
.optimizeAfterPurge
et MassIndexer
.optimizeOnFinish
pour changer ce comportement par défaut.
13.5.1. Optimisation automatique
- un certain nombre d'opérations (insertions et suppressions), ou
- un certain nombre de transactions.
Exemple 13.62. Définition des paramètres d'optimisation automatique
hibernate.search.default.optimizer.operation_limit.max = 1000 hibernate.search.default.optimizer.transaction_limit.max = 100 hibernate.search.Animal.optimizer.transaction_limit.max = 50
Animal
dès que :
- le nombre d'additions et suppressions atteint les
1000
. - le nombre de transactions atteint les
50
(hibernate.search.Animal.optimizer.transaction_limit.max
a la priorité surhibernate.search.default.optimizer.transaction_limit.max
)
org.hibernate.search.store.optimization.OptimizerStrategy
et en déterminant la propriété optimizer.implementation
avec le nom complet de votre implémentation. Cette implémentation doit mettre en place l'interface, être une classe publique et avoir un constructeur public ne prenant aucun argument.
Exemple 13.63. Chargement d'un OptimizerStrategy personnalisé
hibernate.search.default.optimizer.implementation = com.acme.worlddomination.SmartOptimizer hibernate.search.default.optimizer.SomeOption = CustomConfigurationValue hibernate.search.humans.optimizer.implementation = default
default
peut être utilisé pour sélectionner l'implémentation d'Hibernate Search par défaut ; toutes les propriétés après le séparateur de clé .optimizer
seront transférées vers la méthode initialize
de la mise en œuvre au démarrage.
13.5.2. Optimisation manuelle
SearchFactory
:
Exemple 13.64. Optimisation de l'index par programmation
FullTextSession fullTextSession = Search.getFullTextSession(regularSession); SearchFactory searchFactory = fullTextSession.getSearchFactory(); searchFactory.optimize(Order.class); // or searchFactory.optimize();
Order
s et le second optimise tous les index.
Note
searchFactory.optimize()
n'a aucun effet sur un backend JMS. Vous devez appliquer l'opération d'optimisation sur le nœud du Master.
13.5.3. Ajustement d'optimisation
hibernate.search.[default|<indexname>].indexwriter.max_buffered_docs
hibernate.search.[default|<indexname>].indexwriter.max_merge_docs
hibernate.search.[default|<indexname>].indexwriter.merge_factor
hibernate.search.[default|<indexname>].indexwriter.ram_buffer_size
hibernate.search.[default|<indexname>].indexwriter.term_index_interval
13.6. Fonctionnalités avancées
13.6.1. Accès à la SearchFactory
SearchFactory
suit les ressources Lucene sous-jacentes pour Hibernate Search. C'est un moyen efficace d'accéder à Lucene en mode natif. La SearchFactory
peut être accédé à partir d'une FullTextSession
:
Exemple 13.65. Accès à la SearchFactory
FullTextSession fullTextSession = Search.getFullTextSession(regularSession); SearchFactory searchFactory = fullTextSession.getSearchFactory();
13.6.2. Utilisation d'un IndexReader
IndexReader
. Hibernate Search peut effectuer une mise en cache des lecteurs d'index pour maximiser la performance, ou pour fournir d'autres stratégies efficaces pour récupérer un IndexReader
mis à jour en minimisant les opérations E/S. Votre code peut accéder ces ressources mises en cache, mais il existe plusieurs pré-requis.
Exemple 13.66. Accès à un IndexReader
IndexReader reader = searchFactory.getIndexReaderAccessor().open(Order.class); try { //perform read-only operations on the reader } finally { searchFactory.getIndexReaderAccessor().close(reader); }
SearchFactory
détermine les index requis pour effectuer une demande de cette entité (stratégie de partitionnement). L'utilisation de la ReaderProvider
configurée sur chaque index renvoie un composant IndexReader
au-dessus de tous les index impliqués. Cette IndexReader
étant partitionné pour plusieurs clients, vous devez adhérer aux règles suivantes :
- Ne jamais appeler indexReader.close(), mais utiliser plutôt readerProvider.closeReader(reader) si nécessaire, de préférence dans un bloc finally.
- Ne pas utiliser
IndexReader
pour des modifications (il s'agit d'uneIndexReader
: toute modification entraînera une exception).
IndexReader
librement, particulièrement pour effectuer des requêtes Lucene natives. L'utilisation des IndexReader
partagés rendra la plupart des requêtes plus efficaces que si elles étaient ouvertes directement à partir du filesystem par exemple.
open(Class... types)
, vous pouvez utiliser open(String... indexNames)
, une méthode vous permettant de passer dans un ou plusieurs noms d'index. Cette stratégie vous permet également de sélectionner un sous-ensemble d'index pour chaque type indexé si le partitionnement est utilisé.
Exemple 13.67. Accéder à une IndexReader by index names
IndexReader reader = searchFactory.getIndexReaderAccessor().open("Products.1", "Products.3");
13.6.3. Accès à un répertoire Lucene
Répertoire
est l'abstraction la plus courante utilisée par Lucene pour représenter le stockage de l'index ; Hibernate Search n'intéragit pas directement avec un Répertoire
Lucene mais extrait ces interactions par le biais d'un IndexManager
: un index ne doit pas forcément être mis en œuvre par un Répertoire
.
Répertoire
et que vous souhaitez y accéder, vous pouvez obtenir une référence au Répertoire
par le biais de l'IndexManager
. Envoyez l'IndexManager
vers un DirectoryBasedIndexManager
puis utilisez getDirectoryProvider().getDirectory()
pour obtenir une référence au Répertoire
sous-jacent. Cette action n'étant pas recommandée, nous conseillons d'utiliser IndexReader
à la place.
13.6.4. Partitionnement d'indexes
Avertissement
- Un index unique est trop volumineux, donc les temps de mises à jour ralentissent l'application.
- Une recherche typique ne touchera qu'un sous-ensemble d'indexes, comme quand les données sont segmentées naturellement par client, région ou application.
hibernate.search.<indexName>.sharding_strategy.nbr_of_shards
.
Exemple 13.68. Activer le partitionnement de l'index
hibernate.search.<indexName>.sharding_strategy.nbr_of_shards = 5
IndexShardingStrategy
est chargée de diviser les données en sous-indexes. La stratégie de partitionnement par défaut divise les données en fonction de la valeur de hachage de la représentation de la chaîne d'ID (générée par le FieldBridge
). Cela garantit un partitionnement assez équilibré. Vous pouvez remplacer la stratégie par défaut avec une IndexShardingStrategy
personnalisée. Pour utiliser votre statégie personnalisée, vous devez définir la propriété hibernate.search.<indexName>.sharding_strategy
.
Exemple 13.69. Spécifier une stratégie de partitionnement personnalisée
hibernate.search.<indexName>.sharding_strategy = my.shardingstrategy.Implementation
IndexShardingStrategy
permet également d'optimiser les recherches en sélectionnant dans quelle partition (shard) exécuter la requête. En activant un filtre, une stratégie de partitionnement peut sélectionner un sous-ensemble de sections permettant de répondre à une requête (IndexShardingStrategy.getIndexManagersForQuery
) et ainsi accélérer l'exécution de la requête.
IndexManager
indépendant, et donc peut être configuré pour utiliser un fournisseur et un serveur de sauvegarde différents. Les noms d'indexes IndexManager
pour l'entité Animal dans Exemple 13.70, « Configuration de partitionnement pour l'entité Animal » sont Animal.0
à Animal.4
. En d'autres termes, chaque partition porte le nom de l'index auquel il appartient, suivi de .
(point) et son numéro d'index.
Exemple 13.70. Configuration de partitionnement pour l'entité Animal
hibernate.search.default.indexBase = /usr/lucene/indexes hibernate.search.Animal.sharding_strategy.nbr_of_shards = 5 hibernate.search.Animal.directory_provider = filesystem hibernate.search.Animal.0.indexName = Animal00 hibernate.search.Animal.3.indexBase = /usr/lucene/sharded hibernate.search.Animal.3.indexName = Animal03
Animal
en 5 sous indexes. Tous les sous-indexes sont des instances du système de fichiers et du répertoire où est stocké chaque sous-index comme suit :
- pour le sous-index 0 :
/usr/lucene/indexes/Animal00
(indexBase partagée mais indexName substitué) - pour le sous-index 1 :
/usr/lucene/indexes/Animal.1
(indexBase partagé, indexName par défaut) - pour le sous-index 2 :
/usr/lucene/indexes/Animal.1
(indexBase partagé, indexName par défaut) - pour le sous-index 3 :
/usr/lucene/indexes/Animal03
(indexBase substitué, indexName substitué) - pour le sous-index 4 :
/usr/lucene/indexes/Animal.4
(indexBase partagé, indexName par défaut)
IndexShardingStrategy
, n'importe quel champ peut être utilisé pour déterminer la sélection de partitionnement. Considérons que pour traiter des suppressions, avec les opérations purge
et purgeAll
, l'implémentation soit amenée à retourner un ou plusieurs indexes sans être en mesure de lire toutes les valeurs de champ ou l'identificateur principal. Dans ce cas, l'information ne suffit pas pour choisir un seul indice, tous les indexes doivent être retournés, afin que l'opération de suppression puisse être propagée à tout index susceptible de contenir les documents devant être supprimés.
13.6.5. Personnalisation de la formule de notation de Lucene
org.apache.lucene.search.Similarity
. Les méthodes d'abstraction définies dans cette classe correspondent aux facteurs de la formule suivante en calculant la note de la requête q d'un document d :
Facteur | Description |
---|---|
tf(t ind) | Facteur de fréquence de terme pour le terme (t) du document (d). |
idf(t) | Inverse la fréquence du document du terme. |
coord(q,d) | Facteur de score basé sur le nombre de termes de requêtes trouvés dans un document spécifique. |
queryNorm(q) | Facteur de normalisation utilisé pour noter des requêtes comparables. |
t.getBoost() | Boost de champ. |
norm(t,d) | Contient quelques boosts (de temps d'indexation) et facteurs de longueur. |
Similarity
pour plus d'informations.
Similarity
en utilisant la propriété hibernate.search.similarity
. La valeur par défaut est org.apache.lucene.search.DefaultSimilarity
.
similarity
.
hibernate.search.default.similarity = my.custom.Similarity
@Similarity
.
@Entity
@Indexed
@Similarity(impl = DummySimilarity.class)
public class Book {
...
}
tf(float freq)
devrait être 1.0.
Avertissement
Similarity
. Les classes dans la même hiérarchie de classe partagent toujours le même index, il n'est donc pas autorisé de remplacer l'implémentation de Similarity
dans un sous-type.
13.6.6. Configuration pour la gestion des exceptions
hibernate.search.error_handler = log
ErrorHandler
, qui fournit la méthode handle(ErrorContext context)
. ErrorContext
fournit une référence à l'instance principale de LuceneWork
, à l'exception sous-jacente et à toute instance LuceneWork
suivante qui n'aurait pas pu été traitée à cause de l'exception principale.
public interface ErrorContext { List<LuceneWork> getFailingOperations(); LuceneWork getOperationAtFault(); Throwable getThrowable(); boolean hasErrors(); }
ErrorHandler
dans les propriétés de configuration :
hibernate.search.error_handler = CustomerErrorHandler
13.6.7. Désactiver Hibernate Search
Pour désactiver Hibernate Search, définir l'option de configuration indexing_strategy
à manual
, puis redémarrez JBoss EAP.
hibernate.search.indexing_strategy = manual
Pour désactiver Hibernate Search totalement, désactiver tous les listeners en modifiant l'option de configuration autoregister_listeners
à false
, puis redémarrez JBoss EAP.
hibernate.search.autoregister_listeners = false
Chapitre 14. Services Web JAX-RS
14.1. JAX-RS
helloworld-rs
, jax-rs-client
, et kitchensink
quickstart: Section 1.4.1.1, « Accès aux Quickstarts ».
14.2. RESTEasy
14.3. Services web RESTful
14.4. Annotations définies RESTEasy
Tableau 14.1. Annotations JAX-RS/RESTEasy
Annotation | Utilisation |
---|---|
ClientResponseType | Il s'agit d'une annotation que vous pouvez ajouter à une interface de client RESTEasy qui a un type de renvoi Réponse. |
ContentEncoding | Méta annotation qui indique un Contenet-Encoding à appliquer par l'annotation annotée. |
DecorateTypes | Doit être mis sur une classe DecoratorProcessor pour spécifier les types pris en charge. |
Decorator | Méta annotation à mettre dans une autre annotation qui déclenche décoration. |
Form | Peut être utilisé comme objet de valeur pour les requêtes/réponses entrantes/sortantes. |
StringParameterUnmarshallerBinder | Méta annotation à mettre dans une autre annotation qui déclenche un StringParameterUnmarshaller à appliquer à un injecteur d'annotation basé chaîne. |
Cache | Définit l'en-tête Cache-Control de réponse automatiquement. |
NoCache | Définit la réponse Cache-Control à "nocache". |
ServerCached | Indique que la réponse à cette méthode jax-rs devrait être mise en cache sur le serveur. |
ClientInterceptor | Identifie un intercepteur en tant qu'intercepteur côté client. |
DecoderPrecedence | Cet intercepteur est un décodeur Content_Encoding. |
EncoderPrecedence | Cet intercepteur est un encodeur Content_Encoding. |
HeaderDecoratorPrecedence | Les intercepteurs HeaderDecoratorPrecedence doivent toujours venir en premier car ils décorent une réponse (sur le serveur), ou une réquête sortante (sur le client) avec des en-têtes spéciaux définis utilisateur. |
RedirectPrecedence | Doivent être mis dans un PreProcessInterceptor. |
SecurityPrecedence | Doivent être mis dans un PreProcessInterceptor. |
ServerInterceptor | Identifie un intercepteur en tant qu'intercepteur côté client. |
NoJackson | Mis sur une classe, un paramètre, un champ ou une métode quand vous ne souhaitez pas déclencher un fournisseur Jackson. |
ImageWriterParams | Annotation qu'une classe de ressource peut utiliser pour passer des paramètres au IIOImageProvider. |
DoNotUseJAXBProvider | Mettre ceci sur une classe ou sur un paramètre quand vous ne souhaitez pas utiliser le MessageBodyReader/Writer JAXB, mais que vous avez à la place un fournisseur plus spécifique que vous souhaitez utliser pour marshaler le type. |
Formatted | Formate la sortie XML par des indentations et des nouvelles lignes. Il s'agit d'un Decorateur JAXB. |
IgnoreMediaTypes | Mis sur un type, une méthode, un paramètre ou un champ pour indiquer au JAXRS de ne pas utiliser de fournisseur JAXB pour une certain type de media. |
Stylesheet | Indique un en-tête de feuille de style XML. |
Wrapped | Mettre ceci sur une méthode ou un paramètre quand vous souhaitez marshaller ou démarshaller une collection ou un tableau d'objets JAXB. |
WrappedMap | Mettre ceci sur une méthode ou un paramètre quand vous souhaitez marshaller ou démarshaller une mappe d'objets JAXB. |
XmlHeader | Définit un en-tête XML de document renvoyé. |
BadgerFish | Un JSONConfig. |
Mapped | Un JSONConfig. |
XmlNsMap | Un JSONToXml. |
MultipartForm | Peut être utilisé comme objet de valeur pour les requêtes/réponses entrantes/sortantes d'un type de mime multipart/form-data. |
PartType | Doit être utilisé en conjonction avec des fournisseurs Multipart quand on écrit une liste ou un mappage de type multipart/*. |
XopWithMultipartRelated | Cette annotation peut être utilisée pour traiter/produire des messages XOP entrants/sortants (packagés comme multipart/related) vers/en provenance d'objets annotés JAXB. |
After | Utilisée pour ajouter un attribut d'expiration quand on signe ou comme contrôle. |
Signed | Annotation pratique qui déclenche la signature d'une requête ou d'une réponse utilisant la spécification DOSETA. |
Verify | Vérification d'une signature entrante spécifiée dans un en-tête de signature. |
14.5. Configuration RESTEasy
14.5.1. Paramètres de configuration RESTEasy
Tableau 14.2. Éléments
Nom d'option | Valeur par défaut | Description |
---|---|---|
resteasy.servlet.mapping.prefix | Pas de valeur par défaut | Si le modèle d'url du mappage de servlet Resteasy n'est pas /*. |
resteasy.scan | false | Scanner automatiquement les jars WEB-INF/lib et le répertoire WEB-INF/classes pour @Provider et les classes de ressource JAX-RS (@Path, @GET, @POST etc..) à la fois et les enregistrer. |
resteasy.scan.providers | false | Scanner les classes @Provider et les enregistrer. |
resteasy.scan.resources | false | Scanner les classes de ressources JAX-RS. |
resteasy.providers | pas de valeur par défaut | Une liste de noms de classes @Provider complètes que vous souhaitez enregistrer délimitée par des virgules. |
resteasy.use.builtin.providers | true | Indique si on doit ou non enregistrer les classes @Provider intégrées, par défaut. |
resteasy.resources | Pas de valeur par défaut | Une liste de noms de classes de ressources JAX-RS complètes que vous souhaitez enregistrer délimitée par des virgules. |
resteasy.jndi.resources | Pas de valeur par défaut | Une liste délimitée par des virgules de noms JNDI qui référencent des objets que vous souhaitez enregistrer comme ressources JAX-RS. |
javax.ws.rs.Application | Pas de valeur par défaut | Nom qualifié de classe d'application à amorcer en spec portable. |
resteasy.media.type.mappings | Pas de valeur par défaut | Remplace le besoin d'un en-tête Accept par mappage des extensions de noms de fichier (comme .xml ou .txt) avec un type de media. Utilisé quand le client n'est pas en mesure d'utiliser un en-tête Accept pour choisir une représentation (par ex. un navigateur). |
resteasy.language.mappings | Pas de valeur par défaut | Remplace le besoin d'un en-tête Accept-Language par mappage des extensions de noms de fichier (comme .en ou .fr) avec une langue. Utilisé quand le client n'est pas en mesure d'utiliser un en-tête Accept-Language pour choisir une représentation (par ex. un navigateur). |
resteasy.document.expand.entity.references | false | Indique si on doit étendre les entitiés externes ou si on doit les remplacer par une chaine vide. Dans JBoss EAP 6, ce paramètre a comme valeur par défaut false , donc elles sont remplacées par une chaîne vide. |
resteasy.document.secure.processing.feature | true | Impose des contraintes de sécurité pour le traitement des documents org.w3c.dom.Document et des représentations d'objets JAXB. |
resteasy.document.secure.disableDTDs | true | Interdit les DTD dans les documents org.w3c.dom.Document et les représentations d'objets JAXB. |
Important
resteasy.scan.*
du fichier web.xml
seront ignorées, et tous les composants annotés JAX-RS seront scannés automatiquement.
14.6. JAX-RS Web Service Security
14.6.1. Activez la sécurité basée-rôle pour RESTEasy JAX-RS Web Service
RESTEasy supporte les annotations @RolesAllowed, @PermitAll, et @DenyAll sur les méthodes JAX-RS. Cependant, il ne reconnaît pas ces annotations par défaut. Suivre les étapes suivantes pour configurer le fichier web.xml
et pour activer la sécurité basée-rôle.
Avertissement
Procédure 14.1. Activez la sécurité basée-rôle pour RESTEasy JAX-RS Web Service
- Ouvrir le fichier
web.xml
de l'application dans un éditeur de texte. - Ajoutez le <context-param> suivant au fichier, dans les balises
web-app
:<context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param>
- Déclarez tous les rôles utilisés dans le fichier RESTEasy JAX-RS WAR file, en utilisant les balises de <security-role> :
<security-role> <role-name>ROLE_NAME</role-name> </security-role> <security-role> <role-name>ROLE_NAME</role-name> </security-role>
- Autorisez l'accès à tous les URL gérés par le runtime JAX-RS pour tous les rôles :
<security-constraint> <web-resource-collection> <web-resource-name>Resteasy</web-resource-name> <url-pattern>/PATH</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ROLE_NAME</role-name> <role-name>ROLE_NAME</role-name> </auth-constraint> </security-constraint>
La sécurité basée rôle à été activée dans l'application, avec un certain nombre de rôles définis.
Exemple 14.1. Exemple de configuration de sécurité basée rôles
<web-app> <context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <security-constraint> <web-resource-collection> <web-resource-name>Resteasy</web-resource-name> <url-pattern>/security</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>user</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>user</role-name> </security-role> </web-app>
14.6.2. Sécuriser un service JAX-RS Web par des annotations
Cette rubrique couvre les étapes à parcourir pour sécuriser un service JAX-RS Web par les annotations de sécurité supportées.
Procédure 14.2. Sécuriser un service JAX-RS Web par des annotations de sécurité supportées.
- Activez la sécurité basée-rôle. Pour plus d'informations, voir Section 14.6.1, « Activez la sécurité basée-rôle pour RESTEasy JAX-RS Web Service ».
- Ajoutez des annotations de sécurité au service JAX-RS Web. RESTEasy supporte les annotations suivantes :
- @RolesAllowed
- Définit les rôles qui peuvent accéder à la méthode. Tous les rôles doivent être définis dans le fichier
web.xml
. - @PermitAll
- Autorise tous les rôles définis dans le fichier
web.xml
à accéder à la méthode. - @DenyAll
- Refuse tout accès à la méthode.
14.7. Gestion des exceptions
14.7.1. Créer un mappeur d'exceptions
Les mappeurs d'exceptions sont des composants fournis par des applications, personnalisés qui interceptent les exceptions lancées et qui écrivent des réponses HTTP spécifiques.
Exemple 14.2. Mappeur d'exceptions
ExceptionMapper
.
@Provider public class EJBExceptionMapper implements ExceptionMapper<javax.ejb.EJBException> { Response toResponse(EJBException exception) { return Response.status(500).build(); } }
web.xml
sous le paramètre de contexte resteasy.providers
, ou bien renvoyez-le par programmation via la classe ResteasyProviderFactory
.
14.7.2. Exceptions RESTEasy lancées en interne
Tableau 14.3. Liste des exceptions
Exception | Code HTTP | Description |
---|---|---|
BadRequestException | 400 | Échec de la requête. La requête n'a pas été formatée correctement, ou il y a eu un problème lors de son traitement. |
UnauthorizedException | 401 | Non autorisé. Exception de sécurité envoyée si vous utilisez la sécurité basée rôle annotée RESTEasy. |
InternalServerErrorException | 500 | Erreur interne de serveur. |
MethodNotAllowedException | 405 | Il n'y a pas de méthode JAX-RS pour la ressource qui peut gérer l'opération HTTP invoquée. |
NotAcceptableException | 406 | Il n'y a pas de méthode JAX-RS qui puisse produire les types de media listés dans l'en-tête Accept |
NotFoundException | 404 | Il n'y a pas de méthode JAX-RS qui serve le chemin/la ressource de la requête. |
ReaderException | 400 | Toutes les exceptions envoyées, à partir des MessageBodyWriters sont incluses dans cette exception. S'il n'y a pas de ExceptionMapper dans l'exception, ou si l'exception n'est pas une WebApplicationException , alors RESTEasy renverra un code 400 par défaut. |
WriterException | 500 | Toutes les exceptions envoyées, à partir des MessageBodyWriters sont incluses dans cette exception. S'il n'y a pas de ExceptionMapper dans l'exception, ou si l'exception n'est pas une WebApplicationException , alors RESTEasy renverra un code 400 par défaut. |
JAXBUnmarshalException | 400 | Les fournisseurs JAXB (XML et Jettison) envoient cette exception sur les lectures. Peuvent correspondre à des wrapping JAXBExceptions. Cette classe s'étend à ReaderException . |
JAXBMarshalException | 500 | Les fournisseurs JAXB (XML et Jettison) envoient cette exception sur les lectures. Peuvent correspondre à des wrapping JAXBExceptions. Cette classe s'étend à WriterException . |
ApplicationException | S/O | Encapsule toutes les exceptions levées à partir du code d'application. Fonctionne de la même façon que InvocationTargetException . S'il y a un ExceptionMapper pour l'exception encapsulée, elle sera utilisée pour traiter la demande |
Échec | S/O | Erreur RESTEasy interne. Non journalisée. |
LoggableFailure | S/O | Erreur RESTEasy interne. Journalisée. |
DefaultOptionsMethodException | S/O | Si l'utilisateur invoque HTTP OPTIONS et qu'il n'y a pas de méthode JAX-RS, RESTEasy fournira un comportement par défaut en lançant cette exception. |
14.8. Intercepteurs RESTEasy
14.8.1. Interception des invocations JAX-RS
RESTEasy peut intercepter les invocations JAX-RS et les re-router vers des objets style listener que l'on appelle des intercepteurs. Cette section couvre les descriptions de quatre types d'intercepteurs.
Exemple 14.3. Intercepteurs MessageBodyReader/Writer
@Provider
, ainsi que par @ServerInterceptor
ou @ClientInterceptor
, de façon à ce que RESTEasy sache s'il doit les ajouter ou non à la liste d'intercepteurs.
MessageBodyReader.readFrom()
ou de MessageBodyWriter.writeTo()
. Ils peuvent être utilisés pour encapsuler les flux d'entrée ou de sortie.
public interface MessageBodyReaderInterceptor { Object read(MessageBodyReaderContext context) throws IOException, WebApplicationException; } public interface MessageBodyWriterInterceptor { void write(MessageBodyWriterContext context) throws IOException, WebApplicationException; }
MessageBodyReaderContext.proceed()
ou MessageBodyWriterContext.proceed()
est appelé pour aller vers le prochain intercepteur, ou, s'il n'y a plus d'intercepteur à invoquer, la méthode readFrom()
ou writeTo()
du MessageBodyReader ou du MessageBodyWriter. Cette encapsulation permet aux objets d'être modifiés avant d'aller vers le Reader ou Writer, puis d'être nettoyés, après proceed()
.
@Provider @ServerInterceptor public class MyHeaderDecorator implements MessageBodyWriterInterceptor { public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException { context.getHeaders().add("My-Header", "custom"); context.proceed(); } }
Exemple 14.4. PreProcessInterceptor
public interface PreProcessInterceptor { ServerResponse preProcess(HttpRequest request, ResourceMethod method) throws Failure, WebApplicationException; }
preProcess()
renvoie une ServerResponse, alors la méthode JAX-RS sous-jacente ne sera pas invoquée, et le runtime traitera la réponse et retournera au client. Si la méthode preProcess()
ne renvoie pas une ServerResponse, la méthode JAX-RS sous-jacente sera alors invoquée.
Exemple 14.5. PostProcessInterceptors
public interface PostProcessInterceptor { void postProcess(ServerResponse response); }
Exemple 14.6. ClientExecutionInterceptors
@ClientInterceptor
et @Provider
. Ces intercepteurs sont exécutés après que le MessageBodyWriter, et que le ClientRequest aient été créés côté client.
public interface ClientExecutionInterceptor { ClientResponse execute(ClientExecutionContext ctx) throws Exception; } public interface ClientExecutionContext { ClientRequest getRequest(); ClientResponse proceed() throws Exception; }
14.8.2. Lier un intercepteur à une méthode JAX-RS
Par défaut, tous les intercepteurs sont invoqués pour chaque requête. L'interface AcceptedByMethod
peut être implémentée pour préciser ce comportement.
Exemple 14.7. Exemple de liaison d'intercepteurs
accept()
pour les intercepteurs qui implémentent l'interface AcceptedByMethod
. Si la méthode renvoie true, l'intercepteur sera ajouté à la chaîne d'appel de la méthode JAX-RS; sinon, il sera ignoré avec cette méthode.
accept()
détermine si l'annotation @GET est présente dans la méthode JAX-RS. Si tel est le cas, l'intercepteur sera appliqué à la chaîne d'appel de la méthode.
@Provider @ServerInterceptor public class MyHeaderDecorator implements MessageBodyWriterInterceptor, AcceptedByMethod { public boolean accept(Class declaring, Method method) { return method.isAnnotationPresent(GET.class); } public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException { context.getHeaders().add("My-Header", "custom"); context.proceed(); } }
14.8.3. Enregistrer un intercepteur
Cette section couvre la façon dont on doit enregistrer un intercepteur RESTEasy JAX-RS dans une application.
Procédure 14.3. Enregistrer un intercepteur
- Pour enregistrer un intercepteur, listez-le dans le fichier
web.xml
sous le paramètre de contexteresteasy.providers
, ou bien renvoyez-le sous forme de classe ou d'objet dansApplication.getClasses()
ouApplication.getSingletons()
14.8.4. Familles de précédence d'intercepteur
14.8.4.1. Familles de précédence d'intercepteur
Les intercepteurs peuvent être sensibles à l'ordre dans lequel ils sont invoqués. Mettre les intercepteurs de groupes RESTEasy en familles pour faciliter l'ordonnancement. Cette section couvre les familles de précédence d'intercepteurs intégrés et les intercepteurs associés à chaque famille.
- SECURITY
- Les intercepteurs SECURITY sont normalement des PreProcessInterceptors. Ils sont invoqués en premier car on ne doit pas faire grand chose avant que l'invocation soit autorisée.
- HEADER_DECORATOR
- Les intercepteurs HEADER_DECORATOR ajoutent des en-têtes à une réponse ou à une requête sortante. Ils suivent les intercepteurs de sécurité car les en-têtes ajoutés peuvent affecter le comportement des autres familles d'intercepteurs.
- ENCODER
- Les intercepteurs ENCODER changent l'OutputStream. Par exemple, l'intercepteur GZIP crée un GZIPOutputStream pour envelopper l'outputStream réel pour la compression.
- REDIRECT
- Les intercepteurs REDIRECT sont normalement utilisés dans PreProcessInterceptors, car ils peuvent router à nouveau la demande et éviter ainsi totalement la méthode JAX-RS.
- DECODER
- Les intercepteurs DECODER englobent l'IutputStream. Par exemple, le décodeur d'intercepteur GZIP englobe le GZIPInputStream.
org.jboss.resteasy.annotations.interception
du package : @DecoredPrecedence
, @EncoderPrecedence
, @HeaderDecoratorPrecedence
, @RedirectPrecedence
, @SecurityPrecedence
pour la sécurité. Utilisez les à la place de l'annotation @Precedence
.Pour plus d'informations, consultezSection 14.4, « Annotations définies RESTEasy ».
14.8.4.2. Définissez une famille de précédence d'intercepteurs personnalisée
Les familles de précédence personnalisées peuvent être créées ou enregistrées dans le fichier web.xml
. Ce sujet couvre des exemples des paramètres de contexte disponibles pour définir les familles de précédence d'intercepteurs.
Exemple 14.8. resteasy.append.interceptor.precedence
resteasy.append.interceptor.precedence
rajoute la nouvelle famille de précédence à la liste de famille de précédent par défaut.
<context-param> <param-name>resteasy.append.interceptor.precedence</param-name> <param-value>CUSTOM_PRECEDENCE_FAMILY</param-value> </context-param>
Exemple 14.9. resteasy.interceptor.before.precedence
resteasy.interceptor.before.precedence
définit la famille de précédence par défaut exécutée avant la famille personnalisée. La valeur du paramètre est sous la forme DEFAULT_PRECEDENCE_FAMILY/CUSTOM_PRECEDENCE_FAMILY, délimitée par un ':'.
<context-param> <param-name>resteasy.interceptor.before.precedence</param-name> <param-value>DEFAULT_PRECEDENCE_FAMILY : CUSTOM_PRECEDENCE_FAMILY</param-value> </context-param>
Exemple 14.10. resteasy.interceptor.after.precedence
resteasy.interceptor.after.precedence
définit la famille de précédence par défaut exécutée après la famille personnalisée. La valeur du paramètre est sous la forme DEFAULT_PRECEDENCE_FAMILY/CUSTOM_PRECEDENCE_FAMILY, délimitée par un :
.
<context-param> <param-name>resteasy.interceptor.after.precedence</param-name> <param-value>DEFAULT_PRECEDENCE_FAMILY : CUSTOM_PRECEDENCE_FAMILY</param-value> </context-param>
14.9. Annotations basées chaîne
14.9.1. Conversion des annotations basées @*Param en objects
@*Param
JAX-RS, dont @QueryParam, @MatrixParam, @HeaderParam, @PathParam et @FormParam, sont représentées par des chaînes dans une requête HTTP brut. Ces types de paramètres injectés peuvent être convertis en objets si ces objets ont une méthode valueOf(String) statique ou un constructeur qui prend un paramètre String.
@Provider
commerciales pour effectuer cette conversion pour les classes qui n'ont ni de méthode valueOf(String)
statique, ni de constructeur de string.
Exemple 14.11. StringConverter
import org.jboss.resteasy.client.ProxyFactory; import org.jboss.resteasy.spi.StringConverter; import org.jboss.resteasy.test.BaseResourceTest; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.ext.Provider; public class StringConverterTest extends BaseResourceTest { public static class POJO { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } @Provider public static class POJOConverter implements StringConverter<POJO> { public POJO fromString(String str) { System.out.println("FROM STRNG: " + str); POJO pojo = new POJO(); pojo.setName(str); return pojo; } public String toString(POJO value) { return value.getName(); } } @Path("/") public static class MyResource { @Path("{pojo}") @PUT public void put(@QueryParam("pojo")POJO q, @PathParam("pojo")POJO pp, @MatrixParam("pojo")POJO mp, @HeaderParam("pojo")POJO hp) { Assert.assertEquals(q.getName(), "pojo"); Assert.assertEquals(pp.getName(), "pojo"); Assert.assertEquals(mp.getName(), "pojo"); Assert.assertEquals(hp.getName(), "pojo"); } } @Before public void setUp() throws Exception { dispatcher.getProviderFactory().addStringConverter(POJOConverter.class); dispatcher.getRegistry().addPerRequestResource(MyResource.class); } @Path("/") public static interface MyClient { @Path("{pojo}") @PUT void put(@QueryParam("pojo")POJO q, @PathParam("pojo")POJO pp, @MatrixParam("pojo")POJO mp, @HeaderParam("pojo")POJO hp); } @Test public void testIt() throws Exception { MyClient client = ProxyFactory.create(MyClient.class, "http://localhost:8081"); POJO pojo = new POJO(); pojo.setName("pojo"); client.put(pojo, pojo, pojo, pojo); } }
Exemple 14.12. StringParameterUnmarshaller
StringParameterUnmarshaller
est sensible aux annotations placées sur le paramètre ou le champ sur lesquels vous injectez. Il est créé par injecteur. La méthode setAnnotations() est appelée par resteasy pour initialiser l'unmarshaller.
org.jboss.resteasy.annotations.StringsParameterUnmarshallerBinder
.
java.util.Date
basé @PathParam.
public class StringParamUnmarshallerTest extends BaseResourceTest { @Retention(RetentionPolicy.RUNTIME) @StringParameterUnmarshallerBinder(DateFormatter.class) public @interface DateFormat { String value(); } public static class DateFormatter implements StringParameterUnmarshaller<Date> { private SimpleDateFormat formatter; public void setAnnotations(Annotation[] annotations) { DateFormat format = FindAnnotation.findAnnotation(annotations, DateFormat.class); formatter = new SimpleDateFormat(format.value()); } public Date fromString(String str) { try { return formatter.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } } @Path("/datetest") public static class Service { @GET @Produces("text/plain") @Path("/{date}") public String get(@PathParam("date") @DateFormat("MM-dd-yyyy") Date date) { System.out.println(date); Calendar c = Calendar.getInstance(); c.setTime(date); Assert.assertEquals(3, c.get(Calendar.MONTH)); Assert.assertEquals(23, c.get(Calendar.DAY_OF_MONTH)); Assert.assertEquals(1977, c.get(Calendar.YEAR)); return date.toString(); } } @BeforeClass public static void setup() throws Exception { addPerRequestResource(Service.class); } @Test public void testMe() throws Exception { ClientRequest request = new ClientRequest(generateURL("/datetest/04-23-1977")); System.out.println(request.getTarget(String.class)); } }
14.10. Configuration des extensions de fichiers
14.10.1. Mapper les extensions de fichiers avec les types de media dans le fichier web.xml
Certains clients, comme les navigateurs, ne peuvent pas utiliser les en-têtes Accept et Accept-Language pour négocier le type de média de la représentation ou la langue. RESTEasy peut mapper des suffixes de noms de fichier aux langues et types de médias pour régler ce problème. Procédez comme suit pour mapper les types de media avec les extensions de fichier, dans le fichier web.xml
Procédure 14.4. Mapper des types de media avec les extensions de fichiers
- Ouvrir le fichier
web.xml
de l'application dans un éditeur de texte. - Ajouter le paramètre de contexte
resteasy.media.type.mappings
au fichier, dans les balisesweb-app
:<context-param> <param-name>resteasy.media.type.mappings</param-name> </context-param>
- Configurer les valeurs des paramètres. Les mappages forment une liste délimitée par des virgules. Chaque mappage est délimité par un
:
:Exemple 14.13. Exemple de mappage
<context-param> <param-name>resteasy.media.type.mappings</param-name> <param-value>html : text/html, json : application/json, xml : application/xml</param-value> </context-param>
14.10.2. Mapper les extensions de fichiers vers les langues dans le fichier web.xml
Certains clients, comme les navigateurs, ne peuvent pas utiliser les en-têtes Accept et Accept-Language pour négocier le type de média de la représentation ou la langue. RESTEasy peut mapper des suffixes de noms de fichiers aux langues et aux types de médias pour régler ce problème. Procédez comme suit pour mapper les langues avec les extensions de fichier, dans le fichier web.xml
Procédure 14.5. Mapper les extensions de fichiers vers les langues dans le fichier web.xml
- Ouvrir le fichier
web.xml
de l'application dans un éditeur de texte. - Ajouter le paramètre de contexte
resteasy.language.mappings
au fichier, dans les balisesweb-app
:<context-param> <param-name>resteasy.language.mappings</param-name> </context-param>
- Configurer les valeurs des paramètres. Les mappages forment une liste délimitée par des virgules. Chaque mappage est délimité par un
:
:Exemple 14.14. Exemple de mappage
<context-param> <param-name>resteasy.language.mappings</param-name> <param-value> en : en-US, es : es, fr : fr</param-name> </context-param>
14.10.3. Types de media supportés par RESTEasy
Tableau 14.4. Types de media
Type de media | Type Java |
---|---|
application/*+xml, text/*+xml, application/*+json, application/*+fastinfoset, application/atom+* | Classes annotées JaxB |
application/*+xml, text/*+xml | org.w3c.dom.Document |
*/* | java.lang.String |
*/* | java.io.InputStream |
text/brut | primitives, java.lang.String, ou tout type qui possède un contructeur de String, ou une méthode valueOf(String) pour les entrées et, toString() pour les sorties |
*/* | javax.activation.DataSource |
*/* | byte[] |
*/* | java.io.File |
application/x-www-form-urlencoded | javax.ws.rs.core.MultivaluedMap |
14.11. API JavaScript RESTEasy
14.11.1. API JavaScript RESTEasy
Exemple 14.15. Simple exemple d'API JavaScript JAX-RS
@Path("foo") public class Foo{ @Path("{id}") @GET public String get(@QueryParam("order") String order, @HeaderParam("X-Foo") String header, @MatrixParam("colour") String colour, @CookieParam("Foo-Cookie") String cookie){ & } @POST public void post(String text){ } }
var text = Foo.get({order: 'desc', 'X-Foo': 'hello', colour: 'blue', 'Foo-Cookie': 123987235444}); Foo.put({$entity: text});
14.11.2. Activation du Servlet API JavaScript RESTEasy
L'API JavaScript RESTEasy n'est pas activé par défaut. Suivre les étapes suivantes en utilisant le fichier web.xml
.
Procédure 14.6. Modifier web.xml pour activer l'API RESTEasy JavaScript
- Ouvrir le fichier
web.xml
de l'application dans l'éditeur de textes. - Ajouter la configuration suivante dans le fichier, dans les balises
web-app
:<servlet> <servlet-name>RESTEasy JSAPI</servlet-name> <servlet-class>org.jboss.resteasy.jsapi.JSAPIServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RESTEasy JSAPI</servlet-name> <url-pattern>/URL</url-pattern> </servlet-mapping>
14.11.3. Paramètres de l'API JavaScript RESTEasy
Tableau 14.5. Propriétés des paramètres
Property | Valeur par défaut | Description |
---|---|---|
$entity | L'entité pour envoyer une requête PUT ou POST. | |
$contentType | Type MIME de l'entité de contenu envoyée comme en-tête de type de contenu ou Content-Type. Déterminé par l'annotation @Consumes. | |
$accepts | */* | Types MIME de l'entité envoyés comme en-tête de Accept. Déterminé par l'annotation @Provides. |
$callback | Définir à une fonction (httpCode, xmlHttpRequest, value) pour un appel asynchrone. Si non présent, l'appel sera synchronisé et renverra une valeur. | |
$apiURL | Définit la base de l'URI du point de terminaison JAX-RS, sans la dernière barre oblique. | |
$username | Si le nom d'utilisateur ou le mot de passe sont définis, ils seront utilisés comme informations d'authentification pour la demande. | |
$password | Si le nom d'utilisateur ou le mot de passe sont définis, ils seront utilisés comme informations d'authentification pour la demande. |
14.11.4. Créer des requêtes AJAX par l'API JavaScript
L'API JavaScript RESTEasy peut être utilisé pour créer des requêtes manuellement. Cette section couvre des exemples de ce comportement.
Exemple 14.16. L'Objet REST
// Change the base URL used by the API: REST.apiURL = "http://api.service.com"; // log everything in a div element REST.log = function(text){ jQuery("#log-div").append(text); };
- apiURL
- Défini par défaut à l'URL root JAX-RS. Utilisé par chaque fonction d'API Client JavaScript lors de la création de requêtes.
- log
- Définir à une fonction(string) pour pouvoir recevoir les logs d'API Client RESTEasy. Cela est utile si vous souhaitez déboguer votre API Client ou mettre les logs là où vous pouvez les voir.
Exemple 14.17. La classe REST.Request
var r = new REST.Request(); r.setURI("http://api.service.com/orders/23/json"); r.setMethod("PUT"); r.setContentType("application/json"); r.setEntity({id: "23"}); r.addMatrixParameter("JSESSIONID", "12309812378123"); r.execute(function(status, request, entity){ log("Response is "+status); });
14.11.5. Membres de la classe REST.Request
Tableau 14.6. Classe REST.Request
Membre | Description |
---|---|
execute(callback) | Exécute la requête avec toutes les informations définies pour l'objet en cours. La valeur est transmise à la fonction de rappel de l'argument optionnel, et n'est pas renvoyée. |
setAccepts(acceptHeader) | Définit l'en-tête de la requête Accept. Par défaut */*. |
setCredentials(username, password) | Définit les informations d'identification de la requête. |
setEntity(entity) | Définit les informations d'identification de l'entité. |
setContentType(contentTypeHeader) | Définit l'en-tête de la requête Type de contenu |
setURI(uri) | Définit l'URI de la requête. Doit correspondre à un URI absolu. |
setMethod(method) | Définit la méthode de requête. GET par défaut. |
setAsync(async) | Contrôle si la requête doit être asynchrone. True par défaut. |
addCookie(name, value) | Définit le cookie donnée dans le document en cours quand on exécute la requête. Sera persistant dans le navigateur. |
addQueryParameter(name, value) | Ajoute un paramètre d'interrogation à la partie de requête URI. |
addMatrixParameter(name, value) | Ajoute un paramètre de matrix (paramètre de chemin d'accès) au dernier segment de l'URI de la requête. |
addHeader(nom, valeur) | Ajoute un en-tête de requête. |
14.12. RESTEasy Asynchronous Job Service
14.12.1. Service Job Asynchrone RESTEasy
14.12.2. Activer le service de jobs asynchrones
Procédure 14.7. Modifier le fichier web.xml
- Activer le service de jobs asynchrones dans le fichier
web.xml
:<context-param> <param-name>resteasy.async.job.service.enabled</param-name> <param-value>true</param-value> </context-param>
Le service de jobs asynchrones a été activé. Pour les options de configuration, consulter : Section 14.12.4, « Paramètres de configuration de Service Job Asynchrone ».
14.12.3. Configurer les Jobs asynchrones avec RESTEasy
Cette section couvre des exemples de paramètres de recherche de jobs asynchrones avec RESTEasy.
Avertissement
web.xml
à la place.
Important
Exemple 14.18. Le paramètre Asynch
asynch
est utilisé pour exécuter des invocations en arrière-plan. Une réponse 202 Accepted est retourné, ainsi que d'un en-tête d'emplacement avec un URL pointant vers la réponse de la méthode de base.
POST http://example.com/myservice?asynch=true
HTTP/1.1 202 Accepted Location: http://example.com/asynch/jobs/3332334
/asynch/jobs/{job-id}?wait={millisconds}|nowait=true
- GET renvoie la méthode de ressources JAX-RS invoquée comme réponse si le job est complété. Si le job n'a pas été complété, ce GET renverra un code de réponse 202 Accepted. L'invocation de GET ne supprime pas le job; peut être appelé à plusieurs reprises.
- POST procède à une lecture de la réponse du job et supprime le job s'il est terminé.
- DELETE est appelé pour nettoyer manuellement la file d'attente du job.
Note
Quand la file d'attente du job est pleine, il expulsera le job le plus ancien de la mémoire automatiquement, sans avoir besoin d'appeler DELETE.
Exemple 14.19. Wait / Nowait
wait
et nowait
. Si le paramètre wait
n'est pas spécifié, l'opération aura par défaut nowait=true
, et n'attendra pas si le job n'est pas terminé. Le paramètre wait
est défini en millisecondes.
POST http://example.com/asynch/jobs/122?wait=3000
Exemple 14.20. Le Paramètre Oneway
oneway
.
POST http://example.com/myservice?oneway=true
14.12.4. Paramètres de configuration de Service Job Asynchrone
Le tableau suivant donne des informations sur la paramètres contextuels configurables du Service Job Asynchrone. Ces paramètres sont configurés dans le fichier web.xml
.
Tableau 14.7. Paramètres de configuration
Paramètre | Description |
---|---|
resteasy.async.job.service.max.job.results | Nombre de résultats de jobs qui peuvent être conservés en toute harmonie à un moment donné. La valeur par défaut est 100. |
resteasy.async.job.service.max.wait | Temps d'attente maximum quand un client interroge un job. La valeur par défaut est 300000. |
resteasy.async.job.service.thread.pool.size | Taille de thread pool des threads d'arrière-plan qui exécutent le job. La valeur par défaut est 100. |
resteasy.async.job.service.base.path | Définit le chemin de base des URI de job. La valeur par défaut est /asynch/jobs |
Exemple 14.21. Exemple de configuration de jobs asynchrones
<web-app> <context-param> <param-name>resteasy.async.job.service.enabled</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.max.job.results</param-name> <param-value>100</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.max.wait</param-name> <param-value>300000</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.thread.pool.size</param-name> <param-value>100</param-value> </context-param> <context-param> <param-name>resteasy.async.job.service.base.path</param-name> <param-value>/asynch/jobs</param-value> </context-param> <listener> <listener-class> org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap </listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class> org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher </servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
14.13. RESTEasy JAXB
14.13.1. Créer un décorateur JAXB
Les fournisseurs JAXB de RESTEasy ont une manière enfichable de décorer des instances Marshaller ou Unmarshaller. Il y a une annotation qui est créée soit par un Marshaller, soit par un Unmarshaller. Cette section couvre toutes les étapes pour créer un décorateur JAXB dans RESTEasy.
Procédure 14.8. Créer un décorateur JAXB dans RESTEasy
Créer la classe de processeur
- Créer une classe qui implémente DecoratorProcessor<Target, Annotation>. La cible sera soit la classe de Marshaller ou Unmarshaller JAXB. L'annotation est créée dans la seconde étape.
- Annoter la classe par @DecorateTypes, et déclarer les types MIME que le décorateur doit décorer.
- Définir les propriétés ou les valeurs au sein de la fonction
decorate
.
Exemple 14.22. Exemple de classe de processeur
import org.jboss.resteasy.core.interception.DecoratorProcessor; import org.jboss.resteasy.annotations.DecorateTypes; import javax.xml.bind.Marshaller; import javax.xml.bind.PropertyException; import javax.ws.rs.core.MediaType; import javax.ws.rs.Produces; import java.lang.annotation.Annotation; @DecorateTypes({"text/*+xml", "application/*+xml"}) public class PrettyProcessor implements DecoratorProcessor<Marshaller, Pretty> { public Marshaller decorate(Marshaller target, Pretty annotation, Class type, Annotation[] annotations, MediaType mediaType) { target.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } }
Créer une annotation
- Créer une interface personnalisée annotée par @Decorator.
- Déclarer le processeur ou la cible de l'annotation @Decorator. Le processeur est créé dans la première étape. La cible sera soit la classe Marshaller JAXB ou la classe Unmarshaller JAXB.
Exemple 14.23. Exemple d'annotation
import org.jboss.resteasy.annotations.Decorator; @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Decorator(processor = PrettyProcessor.class, target = Marshaller.class) public @interface Pretty {}
- Ajouter l'annotation créée dans une fonction dans la seconde étape de façon à ce que l'entrée ou la sortie soit décorée quand elle sera mise en ordre.
Le décorateur JAXB a été créé et sera appliqué au sein du service web JAX-RS.
14.14. RESTEasy Atom Support
14.14.1. Fournissseur et API Atom
org.jboss.resteasy.plugins.providers.atom
. RESTEasy utilise JAXB pour marshaller et démarshaller l'API. Le fournisseur est basé JAXB, et ne se limite pas à envoyer des objets Atom par XML. Tous les fournisseurs JAXB que RESTEasy contient peuvent être réutilisés par le fournisseur et l'API Atom, y compris JSON. Voir Javadocs pour obtenir plus d'informations sur l'API.
14.15. RESTEasy/Spring Integration
14.15.1. Intégration RESTEasy/Spring
Conditions préalables
- Votre application doit posséder un service JAX-WS et une configuration client.
Procédure 14.9. Activer la fonctionnalité d'intégration RESTEasy/Spring
- RESTEasy s'intègre dans Spring 3.0.x.Les utilisateurs Maven doivent utiliser l'artefact resteasy-spring. Sinon, le jar sera disponible en tant que module dans JBoss EAP 6.RESTEasy est livré avec son propre Spring ContextLoaderListener qui enregistre un BeanPostProcessor spécifique à RESTEasy qui traite les annotations JAX-RS lorsqu'un bean est créé par une BeanFactory. Cela veut dire que RESTEasy recherchera automatiquement les annotations de ressource JAX-RS et @Provider dans votre classe bean et les enregistrera en tant que ressources de JAX-RS.
Exemple 14.24. Modifier web.xml
Ajouter ce qui suit au fichier web.xml pour activer la fonctionnalité d'intégration RESTEasy/Spring :<web-app> <display-name> Archetype Created Web Application </display-name> <listener> <listener-class> org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap </listener-class> </listener> <listener> <listener-class> org.jboss.resteasy.plugins.spring.SpringContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>Resteasy </servlet-name> <servlet-class> org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher </servlet-class> </servlet> <servlet-mapping> <servlet-name> Resteasy </servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Le SpringContextLoaderListener doit être déclaré après ResteasyBootstrap car il utilise les attributs de ServletContext initialisés par celui-ci.
Chapitre 15. Services Web JAX-WS
15.1. Services Web JAX-WS
WS-Notification
, WS-Addressing
, WS-Policy
, WS-Security
et WS-Trust
. Ils communiquent à l'aide d'un XML spécialisé appelé Simple Object Access Protocol (SOAP), qui définit une architecture de message et le message de formats.
WebService
et WebMethod
.
webservices
.
Les JBoss EAP Quickstarts incluent plusieurs applications JAX-WS Web Services qui fonctionnent correctement. Exemples :
- wsat-simple
- wsba-coordinator-completion-simple
- wsba-participant-completion-simple
15.2. Configurer le sous-système des services web
webservices
, qui contrôle le comportement des Services Web déployés dans JBoss EAP 6. La commande pour modifier chaque élément dans le script de gestion CLI (EAP_HOME /bin/jboss-cli.sh
ou EAP_HOME /bin/jboss-cli.bat
) est fournie. Supprimer la partie de la commande /profile=default
pour les serveurs autonomes, ou remplacer default
par le nom de profil à configurer.
Vous pouvez écrire à nouveau l'élément <soap:address>
dans les contrats WSDL endpoint-published. Cette solution peut être utilisée pour contrôler l'adresse du serveur qui est publiée aux clients pour chaque point de terminaison. Chacun des éléments suivants en option peut être modifié pour satisfaire vos besoins. S'il y a un déploiement WS actif, alors toute modification d'un de ces éléments exigera un redémarrage du serveur.
Tableau 15.1. Éléments de configuration pour les adresses de points de terminaison publiés.
Élément | Description | CLI Command |
---|---|---|
modify-wsdl-address |
Indique s'il faut toujours modifier l'adresse WSDL. Si true, le contenu de
< adresse:soap > sera toujours remplacé. Si false, le contenu de < adresse:soap > sera remplacé seulement si ce n'est pas une URL valide. Les valeurs utilisées seront wsdl-host , wsdl-port , et wsdl-secure-port décrit ci-dessous.
| /profile=default/subsystem=webservices/:write-attribute(name=modify-wsdl-address,value=true)
|
wsdl-host |
Le nom d'hôte / adresse IP à utiliser pour écrire à nouveau
<soap:address> . Si wsdl-host est défini au string jbossws.undefined.host , l'hôte du demandeur sera utilisé quand on écrit à nouveau une <soap:address> .
| /profile=default/subsystem=webservices/:write-attribute(name=wsdl-host,value=10.1.1.1) |
wsdl-port | Entier relatif qui définit explicitement le port HTTP qui sera utilisé pour écrire à nouveau l'adresse SOAP. Si non défini, le port HTTP peut être identifié en cherchant la liste de connecteurs HTTP installés. | /profile=default/subsystem=webservices/:write-attribute(name=wsdl-port,value=8080)
|
wsdl-secure-port | Entier relatif qui définit explicitement le port HTTPS qui sera utilisé pour écrire à nouveau l'adresse SOAP. Si non défini, le port HTTPS peut être identifié en cherchant la liste de connecteurs HTTPS installés. | /profile=default/subsystem=webservices/:write-attribute(name=wsdl-secure-port,value=8443)
|
Vous pouvez définir des configurations de points de terminaison qui peuvent être référencées par les implémentations de points de terminaison. Une des façons dont cela puisse être utilisé consiste à ajouter un gestionnaire donné à n'importe quel point de terminaison WS, pour lequel il est indiqué une configuration de point de terminaison donnée avec l'annotation @org.jboss.ws.api.annotation.EndpointConfig
.
Standard-Endpoint-Config
par défaut. Il existe également une configuration Recording-Endpoint-Config
personnalisée, également incluse. Cela vous donne un exemple de gestionnaire d'enregistrement. La Standard-Endpoint-Config
est utilisée automatiquement pour tout point de terminaison non associé à une autre configuration.
Standard-Endpoint-Config
par le Management CLI, il suffit d'utiliser la commande suivante :
/profile=default/subsystem=webservices/endpoint-config=Standard-Endpoint-Config/:read-resource(recursive=true,proxies=false,include-runtime=false,include-defaults=true)
La configuration d'un point de terminaison, à laquelle on fait référence ainsi endpoint-config
dans l'API de gestion, inclut pre-handler-chain
, post-handler-chain
et quelques propriétés qui sont appliquées à un point de terminaison particulier. Les commandes suivantes lisent et ajoutent un point de config.
Exemple 15.1. Lecture d'une config de point de terminaison
/profile=default/subsystem=webservices/endpoint-config=Recording-Endpoint-Config:read-resource
Exemple 15.2. Ajout d'une config de point de terminaison
/profile=default/subsystem=webservices/endpoint-config=My-Endpoint-Config:add
Chaque config de point de terminaison peut être associée à des chaînes de gestionnaires PRE
ou POST
. Chaque chaîne de gestionnaire peut inclure des gestionnaires conformes à JAXWS. Pour les messages sortants, les gestionnaires de chaînes de gestionnaires PRE sont exécutés avant tout gestionnaire attaché aux points de terminaison, à l'aide de moyens JAXWS standards, comme avec l'annotation @HandlerChain
. Les gestionnaires de chaînes de POST handler sont exécutés après les gestionnaires de points de terminaison habituels. Pour les messages entrants, c'est l'opposé. JAX-WS est une API standard pour les services basés XML, et est documenté à l'adresse suivante http://jcp.org/en/jsr/detail?id=224.
protocol-bindings
, qui définit les protocoles qui déclenchent le démarrage de la chaîne.
Exemple 15.3. Lecture d'une chaîne de gestionnaire
/profile=default/subsystem=webservices/endpoint-config=Recording-Endpoint-Config/pre-handler-chain=recording-handlers:read-resource
Exemple 15.4. Ajouter un chaîne de gestionnaire
/profile=default/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-handlers:add(protocol-bindings="##SOAP11_HTTP")
Un gestionnaire JAXWS est un handler
(gestionnaire) d'élément dépendant, qui se trouve à l'intérieur d'une chaîne de gestionnaire. Le gestionnaire prend un attribut de classe
, qui est le nom de classe complet de la classe du gestionnaire. Quand le point de terminaison est déployé, une instance de cette classe sera créée pour chaque déploiement référençant. Le chargeur de classes de déploiement ou le chargeur de classes du module org.jboss.as.webservices.server.integration
doivent pouvoir charger la classe de gestionnaire.
Exemple 15.5. Lecture d'un gestionnaire
/profile=default/subsystem=webservices/endpoint-config=Recording-Endpoint-Config/pre-handler-chain=recording-handlers/handler=RecordingHandler:read-resource
Exemple 15.6. Ajout d'un gestionnaire
/profile=default/subsystem=webservices/endpoint-config=My-Endpoint-Config/post-handler-chain=my-handlers/handler=foo-handler:add(class="org.jboss.ws.common.invocation.RecordingServerHandler")
Vous pouvez afficher des informations d'exécution sur les Services Web, tels que le contexte web et l'URL WSDL, en interrogeant les points de terminaison eux-mêmes. Vous pouvez utiliser le caractère de *
pour interroger tous les points de terminaison à la fois. Les deux exemples suivants montrent la commande à la fois pour un serveur dans un domaine géré, et pour un serveur autonome.
Exemple 15.7. Voir les informations de runtime sur tous les points de terminaison d'un serveur web dans un domaine géré.
server-one
qui se trouve sur l'hôte physique master
et qui exécute sur un domaine géré.
/host=master/server=server-one/deployment="*"/subsystem=webservices/endpoint="*":read-resource
Exemple 15.8. Voir les informations de runtime sur tous les points de terminaison d'un serveur web dans un domaine autonome.
/deployment="*"/subsystem=webservices/endpoint="*":read-resource
Exemple 15.9. Exemple d'information de point de terminaison
{ "outcome" => "success", "result" => [{ "address" => [ ("deployment" => "jaxws-samples-handlerchain.war"), ("subsystem" => "webservices"), ("endpoint" => "jaxws-samples-handlerchain:TestService") ], "outcome" => "success", "result" => { "class" => "org.jboss.test.ws.jaxws.samples.handlerchain.EndpointImpl", "context" => "jaxws-samples-handlerchain", "name" => "TestService", "type" => "JAXWS_JSE", "wsdl-url" => "http://localhost:8080/jaxws-samples-handlerchain?wsdl" } }] }
15.3. Configurer le timeout HTTP par application
- Application - défini dans le fichier de configuration
web.xml
de l'application. - Serveur - indiqué par l'attribut
default-session-timeout
. - Valeur par défaut - 30 minutes.
Procédure 15.1. Configurer le timeout HTTP par application
- Modifiez le fichier
WEB-INF/web.xml
de l'application. - Ajoutez le XML de configuration suivant au fichier, en remplaçant
30
par la valeur de timeout souhaitée (en minutes).<session-config> <session-timeout>30</session-timeout> </session-config>
- Si vous avez modifié le fichier WAR, déployez à nouveau l'application. Si vous éclatez le fichier WAR, aucune autre action ne sera nécessaire car JBoss EAP annulera et relancera automatiquement le déploiement de l'application.
15.4. Ponts de terminaison de services web JAX-WS
15.4.1. Les points de terminaison de Services Web JAX-WS
- Vous pouvez écrire les descripteurs WSDL manuellement.
- Si vous utilisez les annotations de JAX-WS qui créent les descripteurs WSDL automatiquement pour vous. Il s'agit de la méthode la plus commune pour créer des descripteurs WSDL.
Un service web doit se conformer aux conditions préalables de l'API JAXWS et à la spécification des métadonnées des services web qui se trouvent dans http://www.jcp.org/en/jsr/summary?id=181. Une implémentation valide devra remplir les critères suivants :
- Contenir une annotation
javax.jws.WebService
. - Tous les paramètres de méthode et les types de renvoi doivent être compatibles avec la spécification JAXB 2.0, JSR-222. Voir http://www.jcp.org/en/jsr/summary?id=222 pour plus d'informations.
Exemple 15.10. Exemple de point de terminaison POJO
@WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public class JSEBean01 { @WebMethod public String echo(String input) { ... } }
Exemple 15.11. Exemple de point de terminaison de Services Web
<web-app ...> <servlet> <servlet-name>TestService</servlet-name> <servlet-class>org.jboss.test.ws.jaxws.samples.jsr181pojo.JSEBean01</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestService</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Exemple 15.12. Exposer un point de terminaison dans un EJB
@Stateless @Remote(EJB3RemoteInterface.class) @RemoteBinding(jndiBinding = "/ejb3/EJB3EndpointInterface") @WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public class EJB3Bean01 implements EJB3RemoteInterface { @WebMethod public String echo(String input) { ... } }
Les services JAX-WS implémentent généralement une interface de point de terminaison de service Java (SEI), qui peut être mappée à partir d'un type de port WSDL, soit directement ou à l'aide d'annotations. Ce SEI fournit une abstraction de haut niveau qui masque les détails entre les objets Java et leurs représentations XML. Toutefois, dans certains cas, les services doivent pouvoir opérer au niveau du message XML. L'interface de point de terminaison Provider
(fournisseur) fournit cette fonctionnalité aux Services Web qui l'implémentent.
Une fois que vous aurez déployé votre Service Web, vous pourrez consommer le WSDL pour créer les stubs de composants qui seront à la base de votre application. Votre application pourra alors accéder au point de terminaison et faire son travail.
Les JBoss EAP Quickstarts incluent plusieurs applications JAX-WS Web Services qui fonctionnent correctement. Exemples :
- wsat-simple
- wsba-coordinator-completion-simple
- wsba-participant-completion-simple
15.4.2. Écrire et déployer un point de terminaison de Service Web JAX-WS
Cette rubrique décrit le développement d'un simple point de terminaison du service JAX-WS, qui est le composant côté serveur qui répond aux demandes des clients JAX-WS et publie la définition WSDL pour lui-même. Pour plus d'informations sur les points de terminaison de service JAX-WS, consulter Section 15.6.2, « Référence API Commun JAX-WS » et la documentation de l'API en format Javadoc distribuée dans JBoss EAP 6.
Un service web doit se conformer aux pré-requis de l'API JAXWS et à la spécification des métadonnées des services web qui se trouvent dans http://www.jcp.org/en/jsr/summary?id=181. Une implémentation valide devra remplir les critères suivants :
- Contenir une annotation
javax.jws.WebService
. - Tous les paramètres de méthode et les types de renvoi doivent être compatibles avec la spécification JAXB 2.0, JSR-222. Voir http://www.jcp.org/en/jsr/summary?id=222 pour plus d'informations.
Exemple 15.13. Exemple d'implémentation de service
package org.jboss.test.ws.jaxws.samples.retail.profile; import javax.ejb.Stateless; import javax.jws.WebService; import javax.jws.WebMethod; import javax.jws.soap.SOAPBinding; @Stateless @WebService( name="ProfileMgmt", targetNamespace = "http://org.jboss.ws/samples/retail/profile", serviceName = "ProfileMgmtService") @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) public class ProfileMgmtBean { @WebMethod public DiscountResponse getCustomerDiscount(DiscountRequest request) { return new DiscountResponse(request.getCustomer(), 10.00); } }
Exemple 15.14. Exemple de charge XML
DiscountRequest
utilisée par le bean ProfileMgmtBean
dans l'exemple précédent. Les annotations sont incluses par souci de verbosité. Normalement, les valeurs par défaut JAXB sont raisonnables et n'ont pas besoin d'être spécifiées.
package org.jboss.test.ws.jaxws.samples.retail.profile; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; import org.jboss.test.ws.jaxws.samples.retail.Customer; @XmlAccessorType(XmlAccessType.FIELD) @XmlType( name = "discountRequest", namespace="http://org.jboss.ws/samples/retail/profile", propOrder = { "customer" } ) public class DiscountRequest { protected Customer customer; public DiscountRequest() { } public DiscountRequest(Customer customer) { this.customer = customer; } public Customer getCustomer() { return customer; } public void setCustomer(Customer value) { this.customer = value; } }
La classe d'implémentation est encapsulée dans un déploiement de JAR
. Toutes les métadonnées requises pour le déploiement proviennent des annotations qui se trouvent sur la classe d'implémentation et l'interface de point de terminaison de service. Déployer le JAR à l'aide du Management CLI ou de l'Interface de gestion, et le point de terminaison HTTP sera créé automatiquement.
Exemple 15.15. Exemple de structure JAR pour un déploiement de service web
[user@host ~]$ jar -tf jaxws-samples-retail.jar
org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.class
org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.class
org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.class
org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.class
org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtBean.class
org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.class
org/jboss/test/ws/jaxws/samples/retail/profile/package-info.class
15.5. Clients du service JAX-WS Web
15.5.1. Consommer et accéder à un Service Web JAX-WS
- Créer les artefacts client.
- Construire un service stub.
- Accéder au point de terminaison.
Avant de pouvoir créer des artefacts client, vous devrez créer votre contrat WSDL. Le contrat WSDL suivant est utilisé dans les exemples présentés dans le reste de cette section.
ProfileMgmtService.wsdl
.
Exemple 15.16. Exemple de contrat WSDL
<definitions name='ProfileMgmtService' targetNamespace='http://org.jboss.ws/samples/retail/profile' xmlns='http://schemas.xmlsoap.org/wsdl/' xmlns:ns1='http://org.jboss.ws/samples/retail' xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' xmlns:tns='http://org.jboss.ws/samples/retail/profile' xmlns:xsd='http://www.w3.org/2001/XMLSchema'> <types> <xs:schema targetNamespace='http://org.jboss.ws/samples/retail' version='1.0' xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:complexType name='customer'> <xs:sequence> <xs:element minOccurs='0' name='creditCardDetails' type='xs:string'/> <xs:element minOccurs='0' name='firstName' type='xs:string'/> <xs:element minOccurs='0' name='lastName' type='xs:string'/> </xs:sequence> </xs:complexType> </xs:schema> <xs:schema targetNamespace='http://org.jboss.ws/samples/retail/profile' version='1.0' xmlns:ns1='http://org.jboss.ws/samples/retail' xmlns:tns='http://org.jboss.ws/samples/retail/profile' xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:import namespace='http://org.jboss.ws/samples/retail'/> <xs:element name='getCustomerDiscount' nillable='true' type='tns:discountRequest'/> <xs:element name='getCustomerDiscountResponse' nillable='true' type='tns:discountResponse'/> <xs:complexType name='discountRequest'> <xs:sequence> <xs:element minOccurs='0' name='customer' type='ns1:customer'/> </xs:sequence> </xs:complexType> <xs:complexType name='discountResponse'> <xs:sequence> <xs:element minOccurs='0' name='customer' type='ns1:customer'/> <xs:element name='discount' type='xs:double'/> </xs:sequence> </xs:complexType> </xs:schema> </types> <message name='ProfileMgmt_getCustomerDiscount'> <part element='tns:getCustomerDiscount' name='getCustomerDiscount'/> </message> <message name='ProfileMgmt_getCustomerDiscountResponse'> <part element='tns:getCustomerDiscountResponse' name='getCustomerDiscountResponse'/> </message> <portType name='ProfileMgmt'> <operation name='getCustomerDiscount' parameterOrder='getCustomerDiscount'> <input message='tns:ProfileMgmt_getCustomerDiscount'/> <output message='tns:ProfileMgmt_getCustomerDiscountResponse'/> </operation> </portType> <binding name='ProfileMgmtBinding' type='tns:ProfileMgmt'> <soap:binding style='document' transport='http://schemas.xmlsoap.org/soap/http'/> <operation name='getCustomerDiscount'> <soap:operation soapAction=''/> <input> <soap:body use='literal'/> </input> <output> <soap:body use='literal'/> </output> </operation> </binding> <service name='ProfileMgmtService'> <port binding='tns:ProfileMgmtBinding' name='ProfileMgmtPort'> <soap:address location='SERVER:PORT/jaxws-samples-retail/ProfileMgmtBean'/> </port> </service> </definitions>
Note
wsconsume.sh
ou wsconsume.bat
est utilisé pour consommer le contrat abstrait (WSDL) et produire les classes Java annotées et les sources optionnelles qui le définissent. La commande se trouve dans le répertoire EAP_HOME/bin/
de l'installation JBoss EAP 6.
Exemple 15.17. Syntaxe de la commande wsconsume.sh
[user@host bin]$ ./wsconsume.sh --help WSConsumeTask is a cmd line tool that generates portable JAX-WS artifacts from a WSDL file. usage: org.jboss.ws.tools.cmd.WSConsume [options] <wsdl-url> options: -h, --help Show this help message -b, --binding=<file> One or more JAX-WS or JAXB binding files -k, --keep Keep/Generate Java source -c --catalog=<file> Oasis XML Catalog file for entity resolution -p --package=<name> The target package for generated source -w --wsdlLocation=<loc> Value to use for @WebService.wsdlLocation -o, --output=<directory> The directory to put generated artifacts -s, --source=<directory> The directory to put Java source -t, --target=<2.0|2.1|2.2> The JAX-WS specification target -q, --quiet Be somewhat more quiet -v, --verbose Show full exception stack traces -l, --load-consumer Load the consumer and exit (debug utility) -e, --extension Enable SOAP 1.2 binding extension -a, --additionalHeaders Enable processing of implicit SOAP headers -n, --nocompile Do not compile generated sources
.java
listés dans la sortie, à partir du fichier ProfileMgmtService.wsdl
. Les sources utilisent la structure de répertoire du package, qui est spécifié par le commutateur -p
.
[user@host bin]$ wsconsume.sh -k -p org.jboss.test.ws.jaxws.samples.retail.profile ProfileMgmtService.wsdl
output/org/jboss/test/ws/jaxws/samples/retail/profile/Customer.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/package-info.java
output/org/jboss/test/ws/jaxws/samples/retail/profile/Customer.class
output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountRequest.class
output/org/jboss/test/ws/jaxws/samples/retail/profile/DiscountResponse.class
output/org/jboss/test/ws/jaxws/samples/retail/profile/ObjectFactory.class
output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmt.class
output/org/jboss/test/ws/jaxws/samples/retail/profile/ProfileMgmtService.class
output/org/jboss/test/ws/jaxws/samples/retail/profile/package-info.class
.java
et les fichiers .class
compilés sont générés dans le répertoire output/
qui se trouve dans le répertoire où vous exécutez la commande.
Tableau 15.2. Descriptions d'artefacts créés par wsconsume.sh
Fichier | Description |
---|---|
ProfileMgmt.java
|
Interface de point de terminaison de service.
|
Customer.java
|
Personnaliser le type de données.
|
Discount*.java
|
Personnaliser les types de données.
|
ObjectFactory.java
|
Référentiel JAXB XML.
|
package-info.java
|
Annotations de package JAXB.
|
ProfileMgmtService.java
|
Usine de connexions.
|
wsconsume.sh
génère tous les types de données personnalisés (classes JAXB annotées), l'interface de point de terminaison de service et une classe de fabrique de services. Ces artefacts sont utilisés pour créer les implémentations web service clients.
Les clients de service Web permet d'abstraire les détails d'un appel de service web distant stubs de service. Pour une application cliente, une invocation WS ressemble à une invocation de tout autre composant de l'entreprise. Dans ce cas, l'interface de point de terminaison de service agit comme l'interface de l'entreprise, et une classe de fabrique de service n'est pas utilisée pour sa construction comme une ébauche de service.
Exemple 15.18. Créer un Service Stub et accéder à un point de terminaison
wsconsume.sh
pour construire le stub de service. Enfin, le stub peut être utilisé comme le serait toute autre interface commerciale.
import javax.xml.ws.Service; [...] Service service = Service.create( new URL("http://example.org/service?wsdl"), new QName("MyService") ); ProfileMgmt profileMgmt = service.getPort(ProfileMgmt.class); // Use the service stub in your application
15.5.2. Développer une application client JAX-WS
Service
- Aperçu
- Un
Service
est une abstraction qui représente un service WSDL. Un service WSDL est une collection de ports liés les uns aux autres, dont chacun comprend un type de port lié à un protocole particulier et une adresse de point de terminaison particulière.Généralement, le service est créé quand le reste des stubs du composant sont générés à partir d'un contrat WSDL existant. Le contrat WSDL est disponible via l'URL WSDL du point de terminaison déployé, ou peut être créé à partir de la source de point de terminaison à l'aide de la commandewsprovide.sh
dans le répertoire de/bin/ EAP_HOME
.On appelle cela une utilisation statique. Dans un tel cas, vous créez des instances de la classeService
, créée elle-même comme un des composants stubs.Vous pouvez également créer le service manuellement, par la méthodeService.create
. On appelle cela une utilisation dynamic. - Utilisation
- Cas d'utilisation static
- Le cas d'utilisation statique pour un client JAX-WS suppose que vous ayiez déjà un contrat WSDL. Ceci peut être généré par un outil externe ou généré à l'aide des annotations JAX-WS qui conviennent lorsque vous créez votre point de terminaison de JAX-WS.Pour générer vos stubs de composants, vous pouvez utiliser le script
wsconsume.sh
ouwsconsume.bat
inclus dansEAP_HOME/bin/
. Ce script prend le WSDL URL ou fichier en tant que paramètre, et génère de nombreux fichiers structurés dans un arborescence de répertoires. La source et les fichiers de classe représentant votreService
s'appellentCLASSNAME_Service.java
etCLASSNAME_Service.class
, respectivement.La classe d'implémentation générée possède deux constructeurs publics, un sans argument et un avec deux arguments. Les deux arguments représentent respectivement l'emplacement WSDL (unjava.net.URL
) et le nom du service (unjavax.xml.namespace.QName
).Le constructeur sans argument est celui qui est le plus souvent utilisé. Dans ce cas, l'emplacement WSDL et le nom du service sont ceux que l'on trouve dans le fichier WSDL. Ceux-ci sont définis implicitement à partir de l'annotation@WebServiceClient
qui décore la classe générée.Exemple 15.19. Exemple de classe de service générée
@WebServiceClient(name="StockQuoteService", targetNamespace="http://example.com/stocks", wsdlLocation="http://example.com/stocks.wsdl") public class StockQuoteService extends javax.xml.ws.Service { public StockQuoteService() { super(new URL("http://example.com/stocks.wsdl"), new QName("http://example.com/stocks", "StockQuoteService")); } public StockQuoteService(String wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } ... }
- Cas d'utilisation dynamic
- Dans un cas d'utilisation dynamic, aucun stub n'est généré automatiquement. Au lieu de cela, le client de service web utilise la méthode
Service.create
pour créer des instances deService
. Le fragment de code suivant illustre ce processus.Exemple 15.20. Création de service manuellement
URL wsdlLocation = new URL("http://example.org/my.wsdl"); QName serviceName = new QName("http://example.org/sample", "MyService"); Service service = Service.create(wsdlLocation, serviceName);
- Handler Resolver
- JAX-WS fournit un framework plug-in flexible pour des modules de traitement des messages, appelés gestionnaires. Ces gestionnaires étendent les fonctionnalités d'un système de runtime JAX-WS. Une instance de
Service
donnera accès à unHandlerResolver
grâce à une paire de méthodesgetHandlerResolver
etsetHandlerResolver
qui peuvent configurer un ensemble de gestionnaires sur la base de service, de port ou de protocole.Lorsqu'une instance deService
crée un proxy ou une instanceDispatch
, le Handler Resolver enregistré dans le service crée la chaîne de gestionnaires requise. Les changements ultérieurs apportés au Handler Resolver qui sont configurés pour une instance deService
n'affectent pas les gestionnaires sur proxies préalablement créés ou les instances deDispatch
. - Exécuteur
- Les instances de
Service
peuvent être configurées par unjava.util.concurrent.Executor
.Executor
invoque les callbacks asynchrones demandés par l'application. Les méthodessetExecutor
etgetExecutor
deService
peuvent modifier et extraire l'Executor
configuré pour un service.
Un dynamic proxy est une instance de proxy de client utilisant une des méthodes getPort
fournie par le Service
. Le portName
indique le nom du port WSDL que le service utilise. La serviceEndpointInterface
indique l'interface du point de terminaison du service prise en charge par l'instance du proxy dynamique créé.
Exemple 15.21. Méthodes getPort
public <T> T getPort(QName portName, Class<T> serviceEndpointInterface) public <T> T getPort(Class<T> serviceEndpointInterface)
wsconsume.sh
qui interprète le WSDL et qui, à partir de cela, crée des classes Java.
Exemple 15.22. Renvoie le Port d'un Service
@WebServiceClient(name = "TestEndpointService", targetNamespace = "http://org.jboss.ws/wsref", wsdlLocation = "http://localhost.localdomain:8080/jaxws-samples-webserviceref?wsdl") public class TestEndpointService extends Service { ... public TestEndpointService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } @WebEndpoint(name = "TestEndpointPort") public TestEndpoint getTestEndpointPort() { return (TestEndpoint)super.getPort(TESTENDPOINTPORT, TestEndpoint.class); } }
@WebServiceRef
L'annotation @WebServiceRef
déclare une référence à un Service Web. Elle suit un modèle de ressource indiqué par l'annotation javax.annotation.Resource
définie dans http://www.jcp.org/en/jsr/summary?id=250.
Cas d'utilisateur @WebServiceRef
- Vous pouvez l'utiliser pour définir une référence dont le type est une classe de
Service
générée. Dans ce cas, l'élément de valeur et le type font tous deux référence au type de classe deService
généré. En outre, si le type de référence peut être déduit grâce au champ ou à la déclaration de méthode à laquelle l'annotation est s'applique, les éléments de type et de valeur peuvent (mais n'y sont pas tenus) avoir la valeur par défaut deObject.class
. Si le type ne peut pas être déduit, alors l'élément au moins doit être présent, avec une valeur par défaut. - Vous pouvez l'utiliser pour définir une référence de type SEI. Dans ce cas, l'élément de type peut (mais pas forcément) se présenter avec sa valeur par défaut si le type de référence peut être déduit de la déclaration de champ ou de méthode annotée. Toutefois, l'élément de valeur doit toujours être présent et faire référence à un type de classe de service généré, qui est un sous-type de
javax.xml.ws.Service
. L'élémentwsdlLocation
, s'il est présent, substitue les informations d'emplacement WSDL spécifiées dans l'annotation@WebService
de la classe de service générée référencée.Exemple 15.23. Exemples de
@WebServiceRef
public class EJB3Client implements EJB3Remote { @WebServiceRef public TestEndpointService service4; @WebServiceRef public TestEndpoint port3;
Les Services Web XML utilisent des messages XML pour la communication entre le point de terminaison, qui est déployé dans le conteneur Java EE, et tous les clients. Les messages XML utilisent un langage XML appelé Simple Object Access Protocol (SOAP). L'API JAX-WS fournit les mécanismes pour que le point de terminaison et les clients puissent chacun être en mesure d'envoyer et de recevoir des messages SOAP. Le Marshalling, ou conversion de paramètres, est l'action de convertir le message XML SOAP en Objet Java.
Dispatch
fournit cette fonctionnalité. Dispatch
opère dans un des deux modes d'utilisation, qui sont identifiés par l'une des constantes suivantes.
javax.xml.ws.Service.Mode.MESSAGE
- ce mode ordonne aux applications clientes de travailler directement avec les structures de message qui sont spécifiques au protocole. Si utilisé avec une liaison de protocole SOAP, une application cliente fonctionne directement avec un message SOAP.javax.xml.ws.Service.Mode.PAYLOAD
- ce mode amène le client à travailler avec la charge elle-même. Par exemple, s'il est utilisé avec une liaison de protocole SOAP, une application cliente travaillera alors avec le contenu SOAP plutôt qu'avec l'intégralité du message SOAP.
Dispatch
est une API de bas niveau qui exige des clients de structurer les messages ou les charges en XML, avec une adhérence stricte aux normes du protocole individuel et une connaissance approfondie de la structure de message ou de la charge. Dispatch
est une classe générique qui prend en charge l'entrée et la sortie des messages ou des charges de message de n'importe quel type.
Exemple 15.24. Utilisation de Dispatch
Service service = Service.create(wsdlURL, serviceName); Dispatch dispatch = service.createDispatch(portName, StreamSource.class, Mode.PAYLOAD); String payload = "<ns1:ping xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>"; dispatch.invokeOneWay(new StreamSource(new StringReader(payload))); payload = "<ns1:feedback xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>"; Source retObj = (Source)dispatch.invoke(new StreamSource(new StringReader(payload)));
L'interface BindingProvider
représente un composant qui fournit une liaison de protocole que les clients peuvent utiliser. Elle est implémentée et étendue par l'interface Dispatch
.
BindingProvider
peuvent fournir des possibilités d'opérations asynchrones. Comme les opérations asynchrones, les invocations sont découplées de l'instance BindingProvider
au moment de l'invocation. Le contexte de réponse n'est pas mis à jour lorsque l'opération est terminée. Au lieu de cela, un contexte de réponse distinct est fourni à l'aide de l'interface de réponse
.
Exemple 15.25. Invocation asynchrone
public void testInvokeAsync() throws Exception { URL wsdlURL = new URL("http://" + getServerHost() + ":8080/jaxws-samples-asynchronous?wsdl"); QName serviceName = new QName(targetNS, "TestEndpointService"); Service service = Service.create(wsdlURL, serviceName); TestEndpoint port = service.getPort(TestEndpoint.class); Response response = port.echoAsync("Async"); // access future String retStr = (String) response.get(); assertEquals("Async", retStr); }
@Oneway
L'annotation @Oneway
indique que la méthode web donnée ne prend un message d'entrée mais ne renvoie aucun message de sortie. Habituellement, une méthode @Oneway
renvoie le thread de contrôle à l'application appelante avant l'exécution de la méthode commerciale.
Exemple 15.26. Exemple d'invocation @Oneway
@WebService (name="PingEndpoint") @SOAPBinding(style = SOAPBinding.Style.RPC) public class PingEndpointImpl { private static String feedback; @WebMethod @Oneway public void ping() { log.info("ping"); feedback = "ok"; } @WebMethod public String feedback() { log.info("feedback"); return feedback; } }
Il y a deux propriétés qui contrôlent le comportement du délai d'expiration de la connexion HTTP et le délai d'attente d'un client qui attend de recevoir un message. Le premier est javax.xml.ws.client.connectionTimeout
et le second est javax.xml.ws.client.receiveTimeout
. Chacun est exprimé en millisecondes, et la syntaxe correcte est indiquée ci-dessous.
Exemple 15.27. Configuration de timeout JAX-WS
public void testConfigureTimeout() throws Exception { //Set timeout until a connection is established ((BindingProvider)port).getRequestContext().put("javax.xml.ws.client.connectionTimeout", "6000"); //Set timeout until the response is received ((BindingProvider) port).getRequestContext().put("javax.xml.ws.client.receiveTimeout", "1000"); port.echo("testTimeout"); }
15.6. Référence de développement JAX-WS
15.6.1. Activation de WS-Addressing (Web Services Addressing)
Conditions préalables
- Votre application doit posséder un service JAX-WS et une configuration client.
Procédure 15.2. Annoter et mettre à jour le code client
Annoter le point de terminaison du service
Ajouter l'annotation@Addressing
au code du point de terminaison de l'application.Exemple 15.28. annotation
@Addressing
Cet exemple montre un point de terminaison normal avec l'ajout de l'annotation@Addressing
.package org.jboss.test.ws.jaxws.samples.wsa; import javax.jws.WebService; import javax.xml.ws.soap.Addressing; @WebService ( portName = "AddressingServicePort", serviceName = "AddressingService", wsdlLocation = "WEB-INF/wsdl/AddressingService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wsaddressing", endpointInterface = "org.jboss.test.ws.jaxws.samples.wsa.ServiceIface" ) @Addressing(enabled=true, required=true) public class ServiceImpl implements ServiceIface { public String sayHello() { return "Hello World!"; } }
Mise à jour du code client
Mettez à jour le code client dans l'application pour configurer WS-Addressing.Exemple 15.29. La configuration client pour WS-Addressing
Cet exemple montre un client JAX-WS mis à jour pour configurer WS-Addressing.package org.jboss.test.ws.jaxws.samples.wsa; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.soap.AddressingFeature; public final class AddressingTestCase { private final String serviceURL = "http://localhost:8080/jaxws-samples-wsa/AddressingService"; public static void main(String[] args) throws Exception { // construct proxy QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wsaddressing", "AddressingService"); URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); ServiceIface proxy = (ServiceIface)service.getPort(ServiceIface.class, new AddressingFeature()); // invoke method proxy.sayHello(); } }
Le client et le point de terminaison communiquent maintenant avec WS-Addressing.
15.6.2. Référence API Commun JAX-WS
Le framework de gestionnaires est implémenté par une liaison de protocole de JAX-WS dans le runtime du client et le point de terminaison, qui est le composant de serveur. Les proxies et les instances Dispatch
, connus collectivement en tant que fournisseurs de liaison, chacun utilisant des liaisons de protocole pour lier leur fonctionnalité abstraite à des protocoles spécifiques.
Types de gestionnaires de messages
- Gestionnaire logique
- Les gestionnaires logiques n'opèrent que sur des propriétés de contexte de messages et sur des charges utiles de messages. Les gestionnaires logiques sont libérés des protocoles et ne peuvent pas affecter les parties d'un message qui soient spécifiques à un protocole. Les gestionnaires logiques implémentent l'interface
javax.xml.ws.handler.LogicalHandler
. - Gestionnaire de protocoles
- Les gestionnaires de protocoles fonctionnent sur des propriétés de contexte de message et des messages de protocole spécifique. Les gestionnaires de protocoles sont spécifiques à un protocole particulier et peuvent accéder et modifier les aspects spécifiques d'un protocole de message. Les gestionnaires de protocoles implémentent une interface dérivée de
javax.xml.ws.handler.Handler except javax.xml.ws.handler.LogicalHandler
. - Gestionnaire de points de terminaison
- Sur un point de terminaison de service, les gestionnaires sont définis à l'aide de l'annotation
@HandlerChain
. L'emplacement du fichier de chaîne de gestionnaires peut être soit unjava.net.URL
absolu dansexternalForm
ou un chemin d'accès relatif du fichier source ou du fichier de classe.Exemple 15.30. Exemple de gestionnaire de points de terminaison
@WebService @HandlerChain(file = "jaxws-server-source-handlers.xml") public class SOAPEndpointSourceImpl { ... }
- Service Client Handler
- Sur un client JAX-WS, les gestionnaires sont définis soit en utilisant l'annotation
@HandlerChain
, comme dans les points de terminaison de service, soit de façon dynamique, à l'aide de l'API JAX-WS.Exemple 15.31. Définir un Service Client Handler par une API
Service service = Service.create(wsdlURL, serviceName); Endpoint port = (Endpoint)service.getPort(Endpoint.class); BindingProvider bindingProvider = (BindingProvider)port; List<Handler> handlerChain = new ArrayList<Handler>(); handlerChain.add(new LogHandler()); handlerChain.add(new AuthorizationHandler()); handlerChain.add(new RoutingHandler()); bindingProvider.getBinding().setHandlerChain(handlerChain);
L'appel à la méthodesetHandlerChain
est requis.
L'interface MessageContext
est une super interface pour tous les contextes de message JAX-WS. Elle étend Map <String,Object>
de méthodes et des constantes supplémentaires pour gérer un ensemble de propriétés qui permettent aux gestionnaires d'une chaîne de gestionnaires de partager l'état connexe de traitement. Par exemple, un gestionnaire peut utiliser la méthode put
pour insérer une propriété dans le contexte du message. Un ou plusieurs autres gestionnaires de la chaîne de gestionnaires peuvent obtenir par la suite le message via la méthode get
.
APPLICATION
ou GESTIONNAIRE
. Toutes les propriétés sont disponibles pour tous les gestionnaires d'une instance d'un modèle d'échange de messages ou MEP (de l'anglais Message Exchange Pattern) d'un point de terminaison particulier. Par exemple, si un gestionnaire logique met une propriété dans le contexte du message, cette propriété sera également disponible à tous les gestionnaires de protocole dans la chaîne au cours de l'exécution d'une instance MEP.
Note
APPLICATION
sont également rendues disponibles en tant qu'applications clients et implémentations de point de terminaison de services. Le defaultscope
d'une propriété est HANDLER
par défaut.
- Contexte de message logique
- Lorsque les gestionnaires logiques sont invoqués, ils reçoivent un contexte de message de type
LogicalMessageContext
. LeLogicalMessageContext
étendMessageContext
par des méthodes qui récupèrent et modifient la charge du message. Il ne donne pas accès aux aspects spécifiques au protocole d'un message. Une liaison de protocole définit quels composants d'un message sont disponibles via un contexte logique message. Un gestionnaire logique déployé dans une liaison SOAP peut accéder au contenu du corps SOAP, mais pas aux en-têtes SOAP. En revanche, la liaison XML/HTTP définit qu'un gestionnaire de logique accède à l'entière charge XML d'un message. - Contexte de message SOAP
- Quand les gestionnaires SOAP sont invoqués, ils reçoivent un
SOAPMessageContext
. LeSOAPMessageContext
étendMessageContext
par des méthodes qui obtiennent et modifient la charge du message SOAP.
Une application peut lever une SOAPFaultException
ou une exception spécifique à l'application utilisateur. Dans ce dernier cas, les wrapper beans de faute requis seront générés en cours d'exécution s'ils ne font pas déjà partie du déploiement.
Exemple 15.32. Exemple de gestion des fautes
public void throwSoapFaultException() { SOAPFactory factory = SOAPFactory.newInstance(); SOAPFault fault = factory.createFault("this is a fault string!", new QName("http://foo", "FooCode")); fault.setFaultActor("mr.actor"); fault.addDetail().addChildElement("test"); throw new SOAPFaultException(fault); }
public void throwApplicationException() throws UserException { throw new UserException("validation", 123, "Some validation error"); }
Les annotations disponibles via JAX-WS API sont définies dans JSR-224, que l'on peut trouver à l'adresse suivante http://www.jcp.org/en/jsr/detail?id=224. Ces annotations sont dans le package javax.xml.ws
.
javax.jws
.
Chapitre 16. WebSockets
16.1. WebSockets
Upgrade
. Toutes les communications sont alors en full-duplex sur la même connexion TCP/IP, avec une charge minimale de données. Comme chaque message n'inclut pas de contenu d'en-tête HTTP inutile, les communications Websocket nécessitent moins de bandwidth. Le résultat est une voie de communication de faible latence, adaptée aux applications nécessitant une réactivité en temps réel.
16.2. Créer une application WebSocket
- Un client Java ou un client WebSocket : http://caniuse.com/websockets
- Une classe de point de terminaison de serveur WebSocket.
- Un fichier
jboss-web.xml
configuré pour activer WebSockets. - Dépendances de projet configurées pour déclarer une dépendance sur l'API WebSocket.
- Activer le connecteur
NIO2
dans le sous-systèmeweb
du fichier de configuration du serveur Red Hat JBoss Enterprise Application Platform.
Note
Procédure 16.1. Créer l'application WebSocket
Créer un client HTML JavaScript.
Voici un exemple de client WebSocket. Il contient les fonctions JavaScript suivantes :lconnect()
: cette fonction crée la connexion webSocket qui donne l'URI de WebSocket. L'emplacement de la ressource correspond à la ressource définie dans la classe de point de terminaison du serveur. Cette fonction intercepte et gère les WebSocketsonopen
,onmessage
,onerror
, etonclose
.sendMessage()
: cette fonction a le même nom que celui qui se trouve sur le formulaire, crée un message, et l'envoie par la commande WebSocket.send().disconnect()
: cette fonction génère la commande WebSocket.close().displayMessage()
: cette fonction détermine le message affiché sur la page à la valeur donnée par la méthode de point de terminaison de WebSocket.displayStatus()
: cette fonction affiche le statut de la connexion de WebSocket.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>WebSocket: Say Hello</title> <link rel="stylesheet" type="text/css" href="resources/css/hello.css" /> <script type="text/javascript"> var websocket = null; function connect() { var wsURI = 'ws://' + window.location.host + '/jboss-websocket-hello/websocket/helloName'; websocket = new WebSocket(wsURI); websocket.onopen = function() { displayStatus('Open'); document.getElementById('sayHello').disabled = false; displayMessage('Connection is now open. Type a name and click Say Hello to send a message.'); }; websocket.onmessage = function(event) { // log the event displayMessage('The response was received! ' + event.data, 'success'); }; websocket.onerror = function(event) { // log the event displayMessage('Error! ' + event.data, 'error'); }; websocket.onclose = function() { displayStatus('Closed'); displayMessage('The connection was closed or timed out. Please click the Open Connection button to reconnect.'); document.getElementById('sayHello').disabled = true; }; } function disconnect() { if (websocket !== null) { websocket.close(); websocket = null; } message.setAttribute("class", "message"); message.value = 'WebSocket closed.'; // log the event } function sendMessage() { if (websocket !== null) { var content = document.getElementById('name').value; websocket.send(content); } else { displayMessage('WebSocket connection is not established. Please click the Open Connection button.', 'error'); } } function displayMessage(data, style) { var message = document.getElementById('hellomessage'); message.setAttribute("class", style); message.value = data; } function displayStatus(status) { var currentStatus = document.getElementById('currentstatus'); currentStatus.value = status; } </script> </head> <body> <div> <h1>Welcome to JBoss!</h1> <div>This is a simple example of a WebSocket implementation.</div> <div id="connect-container"> <div> <fieldset> <legend>Connect or disconnect using WebSocket :</legend> <input type="button" id="connect" onclick="connect();" value="Open Connection" /> <input type="button" id="disconnect" onclick="disconnect();" value="Close Connection" /> </fieldset> </div> <div> <fieldset> <legend>Type your name below. then click the `Say Hello` button :</legend> <input id="name" type="text" size="40" style="width: 40%"/> <input type="button" id="sayHello" onclick="sendMessage();" value="Say Hello" disabled="disabled"/> </fieldset> </div> <div>Current WebSocket Connection Status: <output id="currentstatus" class="message">Closed</output></div> <div> <output id="hellomessage" /> </div> </div> </div> </body> </html>
Créer le point de terminaison du serveur de WebSocket.
Vous pouvez créer un point de terminaison de serveur WebSocket par l'une des méthodes suivantes.L'exemple de code ci-dessous utilise une approche de point de terminaison annoté et gère les événements suivants.Programmatic Endpoint
: le point de terminaison prolonge la classe de point de terminaison.Annotated Endpoint
: la classe de point de terminaison utilise des annotations pour interagir avec les événements WebSocket. Plus facile à codifier qu'un point de terminaison programmatique.
- L'annotation
@ServerEndpoint
identifie cette classe comme point de terminaison de serveur WebSocket et indique le chemin d'accès. - L'annotation
@OnOpen
s'active quand la connexion de WebSocket est ouverte. - L'annotation
@OnMessage
s'active quand un message est envoyé vers une connexion de WebSocket. - L'annotation
@OnClose
s'active quand la connexion de WebSocket est fermée.
package org.jboss.as.quickstarts.websocket_hello; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/websocket/helloName") public class HelloName { @OnMessage public String sayHello(String name) { System.out.println("Say hello to '" + name + "'"); return ("Hello" + name); } @OnOpen public void helloOnOpen(Session session) { System.out.println("WebSocket opened: " + session.getId()); } @OnClose public void helloOnClose(CloseReason reason) { System.out.println("Closing a WebSocket due to " + reason.getReasonPhrase()); } }
Configurer le fichier jboss-web.xml.
Vous devez créer l'élément<enable-websockets>
dans l'applicationWEB-INF/jboss-web.xml
et la définir àtrue
.<?xml version="1.0" encoding="UTF-8"?> <!--Enable WebSockets --> <jboss-web> <enable-websockets>true</enable-websockets> </jboss-web>
Déclarer la dépendance de l'API de Websocket dans votre fichier POM de projet.
Si vous utilisez Maven, vous devrez ajouter la dépendance suivante au fichierpom.xml
du projet.<dependency> <groupId>org.jboss.spec.javax.websocket</groupId> <artifactId>jboss-websocket-api_1.0_spec</artifactId> <version>1.0.0.Final</version> <scope>provided</scope> </dependency>
Configurer le serveur JBoss EAP.
Configurer le<connector>
http
dans le sous-systèmeweb
du fichier de configuration du serveur pour qu'il puisse utliser le protocoleNIO2
.- Démarrer le serveur JBoss EAP.
- Lancer l'interface CLI par la commande pour votre système d'exploitation.Dans Linux :
Dans Windows :EAP_HOME/bin/jboss-cli.sh --connect
EAP_HOME\bin\jboss-cli.bat --connect
- Pour activer le protocole connecteur
NIO2
Java non-bloquant dans le sous-systèmeweb
du fichier de configuration du serveur JBoss EAP, saisissez la commande suivante./subsystem=web/connector=http/:write-attribute(name=protocol,value=org.apache.coyote.http11.Http11NioProtocol)
Quelle que soit la commande dont il s'agit, vous devrez voir le résultat suivant apparaître :{ "outcome" => "success", "response-headers" => { "operation-requires-reload" => true, "process-state" => "reload-required" } }
- Indiquer au serveur de charger la configuration à nouveau.
:reload
Vous devriez voir apparaître le résultat suivant :{ "outcome" => "success", "result" => undefined }
- Réviser les changements apportés au fichier de configuration du serveur JBoss EAP. Le sous-système
web
doit maintenant contenir l'XML suivant pour le<connector>
http
.<subsystem xmlns="urn:jboss:domain:web:2.1" default-virtual-server="default-host" native="false"> <connector name="http" protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="http" socket-binding="http"/> <virtual-server name="default-host" enable-welcome-root="true"> <alias name="localhost"/> <alias name="example.com"/> </virtual-server> </subsystem>
Chapitre 17. Identité au sein d'applications
17.1. Concepts de base
17.1.1. Cryptage
17.1.2. Les domaines de sécurité
17.1.3. Cryptage SSL
Avertissement
17.1.4. Sécurité déclarative
17.2. Sécurité basée-rôle pour les applications
17.2.1. La sécurité des applications
17.2.2. Authentification
17.2.3. L'autorisation
17.2.4. Security Auditing
17.2.5. Mappage de sécurité
17.2.6. Java Authentication et Authorization Service (JAAS)
17.2.7. Java Authentication et Authorization Service (JAAS)
Les groupes de serveurs (dans un domaine géré) et les serveurs (dans un serveur autonome) comprennent une configuration des domaines de la sécurité. Un domaine de sécurité comprend des informations sur une combinaison d'authentification, d'autorisation, de mappage et de modules, avec détails de configuration. Une application spécifie quel domaine de sécurité est exigé, par son nom, dans son fichier jboss-web.xml
.
Une configuration spécifique à une application a lieu dans un ou plusieurs fichiers.
Tableau 17.1. Fichiers de configuration spécifique à une application
Fichier | Description |
---|---|
ejb-jar.xml |
Le descripteur de déploiement pour une application Enterprise JavaBeans (EJB), situé dans le répertoire
META-INF de l'archive. Utiliser le fichier ejb-jar.xml pour préciser les rôles et les mapper aux principaux, au niveau de l'application. Vous pouvez également limiter certaines méthodes et classes spécifiques à certains rôles. Également utilisé pour les autres configurations EJB spécifiques non liées à la sécurité.
|
web.xml |
Le descripteur de déploiement pour une application web en Java Enterprise Edition (EE). Utilisez le fichier
web.xml pour déclarer les contraintes de transport et ressources, comme limiter les types de demandes HTTP autorisées. Vous pouvez également configurer l'authentification basée-web dans ce fichier. Utilisé également pour d'autres configurations spécifiques à l'application non liées à la sécurité. Le domaine de sécurité que l'application utilise pour l'authentification et l'autorisation est défini dans jboss-web.xml .
|
jboss-ejb3.xml |
Contient des extensions au descripteur
ejb-jar.xml spécifiques à JBoss.
|
jboss-web.xml |
Contient des extensions au descripteur
web.xml spécifiques à JBoss.
|
Note
ejb-jar.xml
et web.xml
sont définis dans la spécification Java Enterprise Edition (Java EE). Le fichier jboss-ejb3.xml
fournit des extensions spécifiques à JBoss pour le fichier ejb-jar.xml
, et le fichier jboss-web.xml
fournit des extensions spécifiques à JBoss pour le fichier web.xml
.
17.2.8. Utiliser un domaine de sécurité dans votre application
Pour utiliser un domaine de sécurité dans votre application, vous devez tout d'abord définir le domaine dans le fichier de configuration du serveur, puis vous devez l'activer pour une application dans le descripteur de déploiement de l'application. Ensuite, vous devez ajouter les annotations requises à l'EJB qui les utilise. Cette rubrique décrit les étapes requises pour utiliser un domaine de sécurité dans votre application.
Avertissement
Procédure 17.1. Configurez votre application pour qu'elle puisse utiliser un domaine de sécurité
Définir le domaine de sécurité
Vous devez définir le domaine de sécurité dans le fichier de configuration du serveur, puis l'activer pour une application dans le fichier du descripteur de l'application.Configurez le domaine de sécurité dans le fichier de configuration du serveur
Le domaine de sécurité est configuré dans le sous-système desécurité
du fichier de configuration du serveur. Si l'instance de JBoss EAP 6 s'exécute dans un domaine géré, il s'agira du fichierdomain/configuration/domain.xml
. Si l'instance de JBoss EAP 6 s'exécute comme un serveur autonome, ce sera le fichierstandalone/configuration/standalone.xml
.Les domaines de sécuritéother
,jboss-web-policy
, etjboss-ejb-policy
sont fournis par défaut dans JBoss EAP 6. L'exemple XML suivant a été copié à partir du sous-système desécurité
dans le fichier de configuration du serveur.L'attributcache-type
d'un domaine de sécurité spécifie un cache pour pouvoir effectuer des contrôles d'authentification plus rapides. Les valeur autorisées sont les valeurs pardéfaut
en cas de simple mappe comme cache ouinfinispan
pour un cache Infinispan.<subsystem xmlns="urn:jboss:domain:security:1.2"> <security-domains> <security-domain name="other" cache-type="default"> <authentication> <login-module code="Remoting" flag="optional"> <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> <security-domain name="jboss-web-policy" cache-type="default"> <authorization> <policy-module code="Delegating" flag="required"/> </authorization> </security-domain> <security-domain name="jboss-ejb-policy" cache-type="default"> <authorization> <policy-module code="Delegating" flag="required"/> </authorization> </security-domain> </security-domains> </subsystem>
Vous pouvez configurer des domaines de sécurité supplémentaires selon les besoins par la console de gestion ou par l'interface CLI.Activer le domaine de sécurité dans le fichier de descripteur de l'application.
Le domaine de sécurité est spécifié dans l'élément enfant<security-domain>
de l'élément<jboss-web>
du fichierWEB-INF/jboss-web.xml
de l'application. L'exemple suivant configure un domaine de sécurité nommémy-domain
.<jboss-web> <security-domain>my-domain</security-domain> </jboss-web>
Il s'agit d'une des configurations que vous pouvez indiquer dans le descripteurWEB-INF/jboss-web.xml
.
Ajoutez l'annotation requise à l'EJB.
Vous pouvez configurer la sécurité dans EJB par les annotations@SecurityDomain
et@RolesAllowed
. L'exemple de code EJB suivant limite l'accès au domaine de sécuritéother
aux utilisateurs ayant pour rôleguest
(invité).package example.ejb3; import java.security.Principal; import javax.annotation.Resource; import javax.annotation.security.RolesAllowed; import javax.ejb.SessionContext; import javax.ejb.Stateless; import org.jboss.ejb3.annotation.SecurityDomain; /** * Simple secured EJB using EJB security annotations * Allow access to "other" security domain by users in a "guest" role. */ @Stateless @RolesAllowed({ "guest" }) @SecurityDomain("other") public class SecuredEJB { // Inject the Session Context @Resource private SessionContext ctx; /** * Secured EJB method using security annotations */ public String getSecurityInfo() { // Session context injected using the resource annotation Principal principal = ctx.getCallerPrincipal(); return principal.toString(); } }
Pour obtenir des exemples de code supplémentaires, voirejb-security
Quickstart dans le package JBoss EAP 6 Quickstarts disponible à partir du portail clients de Red Hat.
17.2.9. Utilisation de la sécurité basée-rôle dans les Servlets
jboss-web.xml
du WAR.
Avant d'utiliser la sécurité basée-rôles dans un servlet, le domaine de sécurité utilisé pour authentifier et autoriser l'accès doit être configuré sur la plateforme JBoss EAP 6.
Procédure 17.2. Ajout de la sécurité basée-rôle dans les servlets
Ajout de mappages entre les types d'URL et les servlets.
Utiliser les éléments<servlet-mapping>
du fichierweb.xml
pour mapper les servlets individuels à des types d'URL. L'exemple suivant mappe le serveur nomméDisplayOpResult
au type d'URL/DisplayOpResult
.<servlet-mapping> <servlet-name>DisplayOpResult</servlet-name> <url-pattern>/DisplayOpResult</url-pattern> </servlet-mapping>
Ajout des contraintes de sécurité aux types d'URL.
Pour mapper le type d'URL avec une contrainte de sécurité, utilisez un<security-constraint>
. L'exemple suivant limite l'accès d'un type d'URL/DisplayOpResult
afin qu'il soit accessible aux principaux ayant pour rôleeap_admin
. Le rôle doit être présent dans le domaine de sécurité.<security-constraint> <display-name>Restrict access to role eap_admin</display-name> <web-resource-collection> <web-resource-name>Restrict access to role eap_admin</web-resource-name> <url-pattern>/DisplayOpResult/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>eap_admin</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>eap_admin</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> </login-config>
Vous aurez besoin d'indiquer la méthode d'authentification, qui peut être une des méthodes suivantes :BASIC, FORM, DIGEST, CLIENT-CERT, SPNEGO.
. Cet exemple utilise l'authentificationBASIC
.Indiquez le domaine de sécurité et le fichier
jboss-web.xml
du WAR.Ajoutez le domaine de sécurité au fichierjboss-Web.xml
du WAR afin de connecter les servlets au domaine de la sécurité configuré sachant comment authentifier et autoriser les principaux selon les contraintes de sécurité. L'exemple suivant utilise le domaine de sécurité appeléacme_domain
.<jboss-web> ... <security-domain>acme_domain</security-domain> ... </jboss-web>
Exemple 17.1. Exemple web.xml
avec la sécurité basée rôle configurée.
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Use Role-Based Security In Servlets</display-name> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <servlet-mapping> <servlet-name>DisplayOpResult</servlet-name> <url-pattern>/DisplayOpResult</url-pattern> </servlet-mapping> <security-constraint> <display-name>Restrict access to role eap_admin</display-name> <web-resource-collection> <web-resource-name>Restrict access to role eap_admin</web-resource-name> <url-pattern>/DisplayOpResult/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>eap_admin</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>eap_admin</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> </login-config> </web-app>
17.2.10. Utilisation d'une authentification système de tierce partie pour votre application
Note
context.xml
. Les valves sont configurées directement dans le descripteur jboss-web.xml
à la place. Le fichier context.xml
peut maintenant être ignoré.
Exemple 17.2. Valve d'authentification de base
<jboss-web> <valve> <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name> </valve> </jboss-web>
Exemple 17.3. Personnaliser une valve avec des attributs d'en-tête
<jboss-web> <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> </jboss-web>
Rédiger vous-même votre authentificateur est en dehors de la portée de ce document. Cependant, le code Java suivant est fourni comme exemple.
Exemple 17.4. GenericHeaderAuthenticator.java
/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.web.tomcat.security; import java.io.IOException; import java.security.Principal; import java.util.StringTokenizer; import javax.management.JMException; import javax.management.ObjectName; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Realm; import org.apache.catalina.Session; import org.apache.catalina.authenticator.Constants; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; import org.jboss.logging.Logger; import org.jboss.as.web.security.ExtendedFormAuthenticator; /** * JBAS-2283: Provide custom header based authentication support * * Header Authenticator that deals with userid from the request header Requires * two attributes configured on the Tomcat Service - one for the http header * denoting the authenticated identity and the other is the SESSION cookie * * @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a> * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a> * @version $Revision$ * @since Sep 11, 2006 */ public class GenericHeaderAuthenticator extends ExtendedFormAuthenticator { protected static Logger log = Logger .getLogger(GenericHeaderAuthenticator.class); protected boolean trace = log.isTraceEnabled(); // JBAS-4804: GenericHeaderAuthenticator injection of ssoid and // sessioncookie name. private String httpHeaderForSSOAuth = null; private String sessionCookieForSSOAuth = null; /** * <p> * Obtain the value of the <code>httpHeaderForSSOAuth</code> attribute. This * attribute is used to indicate the request header ids that have to be * checked in order to retrieve the SSO identity set by a third party * security system. * </p> * * @return a <code>String</code> containing the value of the * <code>httpHeaderForSSOAuth</code> attribute. */ public String getHttpHeaderForSSOAuth() { return httpHeaderForSSOAuth; } /** * <p> * Set the value of the <code>httpHeaderForSSOAuth</code> attribute. This * attribute is used to indicate the request header ids that have to be * checked in order to retrieve the SSO identity set by a third party * security system. * </p> * * @param httpHeaderForSSOAuth * a <code>String</code> containing the value of the * <code>httpHeaderForSSOAuth</code> attribute. */ public void setHttpHeaderForSSOAuth(String httpHeaderForSSOAuth) { this.httpHeaderForSSOAuth = httpHeaderForSSOAuth; } /** * <p> * Obtain the value of the <code>sessionCookieForSSOAuth</code> attribute. * This attribute is used to indicate the names of the SSO cookies that may * be present in the request object. * </p> * * @return a <code>String</code> containing the names (separated by a * <code>','</code>) of the SSO cookies that may have been set by a * third party security system in the request. */ public String getSessionCookieForSSOAuth() { return sessionCookieForSSOAuth; } /** * <p> * Set the value of the <code>sessionCookieForSSOAuth</code> attribute. This * attribute is used to indicate the names of the SSO cookies that may be * present in the request object. * </p> * * @param sessionCookieForSSOAuth * a <code>String</code> containing the names (separated by a * <code>','</code>) of the SSO cookies that may have been set by * a third party security system in the request. */ public void setSessionCookieForSSOAuth(String sessionCookieForSSOAuth) { this.sessionCookieForSSOAuth = sessionCookieForSSOAuth; } /** * <p> * Creates an instance of <code>GenericHeaderAuthenticator</code>. * </p> */ public GenericHeaderAuthenticator() { super(); } public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException { log.trace("Authenticating user"); Principal principal = request.getUserPrincipal(); if (principal != null) { if (trace) log.trace("Already authenticated '" + principal.getName() + "'"); return true; } Realm realm = context.getRealm(); Session session = request.getSessionInternal(true); String username = getUserId(request); String password = getSessionCookie(request); // Check if there is sso id as well as sessionkey if (username == null || password == null) { log.trace("Username is null or password(sessionkey) is null:fallback to form auth"); return super.authenticate(request, response, config); } principal = realm.authenticate(username, password); if (principal == null) { forwardToErrorPage(request, response, config); return false; } session.setNote(Constants.SESS_USERNAME_NOTE, username); session.setNote(Constants.SESS_PASSWORD_NOTE, password); request.setUserPrincipal(principal); register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password); return true; } /** * Get the username from the request header * * @param request * @return */ protected String getUserId(Request request) { String ssoid = null; // We can have a comma-separated ids String ids = ""; try { ids = this.getIdentityHeaderId(); } catch (JMException e) { if (trace) log.trace("getUserId exception", e); } if (ids == null || ids.length() == 0) throw new IllegalStateException( "Http headers configuration in tomcat service missing"); StringTokenizer st = new StringTokenizer(ids, ","); while (st.hasMoreTokens()) { ssoid = request.getHeader(st.nextToken()); if (ssoid != null) break; } if (trace) log.trace("SSOID-" + ssoid); return ssoid; } /** * Obtain the session cookie from the request * * @param request * @return */ protected String getSessionCookie(Request request) { Cookie[] cookies = request.getCookies(); log.trace("Cookies:" + cookies); int numCookies = cookies != null ? cookies.length : 0; // We can have comma-separated ids String ids = ""; try { ids = this.getSessionCookieId(); log.trace("Session Cookie Ids=" + ids); } catch (JMException e) { if (trace) log.trace("checkSessionCookie exception", e); } if (ids == null || ids.length() == 0) throw new IllegalStateException( "Session cookies configuration in tomcat service missing"); StringTokenizer st = new StringTokenizer(ids, ","); while (st.hasMoreTokens()) { String cookieToken = st.nextToken(); String val = getCookieValue(cookies, numCookies, cookieToken); if (val != null) return val; } if (trace) log.trace("Session Cookie not found"); return null; } /** * Get the configured header identity id in the tomcat service * * @return * @throws JMException */ protected String getIdentityHeaderId() throws JMException { if (this.httpHeaderForSSOAuth != null) return this.httpHeaderForSSOAuth; return (String) mserver.getAttribute(new ObjectName( "jboss.web:service=WebServer"), "HttpHeaderForSSOAuth"); } /** * Get the configured session cookie id in the tomcat service * * @return * @throws JMException */ protected String getSessionCookieId() throws JMException { if (this.sessionCookieForSSOAuth != null) return this.sessionCookieForSSOAuth; return (String) mserver.getAttribute(new ObjectName( "jboss.web:service=WebServer"), "SessionCookieForSSOAuth"); } /** * Get the value of a cookie if the name matches the token * * @param cookies * array of cookies * @param numCookies * number of cookies in the array * @param token * Key * @return value of cookie */ protected String getCookieValue(Cookie[] cookies, int numCookies, String token) { for (int i = 0; i < numCookies; i++) { Cookie cookie = cookies[i]; log.trace("Matching cookieToken:" + token + " with cookie name=" + cookie.getName()); if (token.equals(cookie.getName())) { if (trace) log.trace("Cookie-" + token + " value=" + cookie.getValue()); return cookie.getValue(); } } return null; } }
17.3. Domaines de sécurité
17.3.1. Domaines de sécurité
ManagementRealm
stocke les informations d'authentification pour l'API de gestion, qui fournit les fonctionnalités pour l'interface CLI et la Console de gestion sur le web. Il fournit un système d'authentification pour gérer JBoss EAP 6 . Vous pouvez également utiliser leManagementRealm
si votre application a besoin des mêmes règles commerciales que vous utilisez pour l'API de gestion, lors de son authentification.ApplicationRealm
stocke l'utilisateur, le mot de passe et les informations de rôle pour les applications Web et les EJB.
REALM-users.properties
stocke les mots de passe et les mots de passe hachés.REALM-roles.properties
stocke les mappages user-to-role.mgmt-groups.properties
stocke le fichier de mappage user-to-group pourManagementRealm
. Utilisé uniquement quand RBAC (Role-based Access Control) est activé.
domain/configuration/
et standalone/configuration/
. Les fichiers sont inscrits simultanément par la commande add-user.sh
ou add-user.bat
. Quand vous exécutez la commande, la première décision est de décider dans quel domaine ajouter votre premier utilisateur.
17.3.2. Ajout d'un domaine de sécurité
Exécuter l'interface CLI
Démarrez par la commandejboss-cli.sh
oujboss-cli.bat
et connectez-vous au serveur.Créer le nouveau domaine de sécurité lui-même.
Exécutez la commande suivante pour créer un nouveau domaine de sécurité nomméMyDomainRealm
sur un contrôleur de domaine ou sur un serveur autonome.Pour une instance de domaine, utiliser la commande suivante :/host=master/core-service=management/security-realm=MyDomainRealm:add()
Pour une instance autonome, utiliser la commande suivante :/core-service=management/security-realm=MyDomainRealm:add()
Créer les références du fichier de propriétés qui stocke les informations sur le nouveau rôle.
Exécutez la commande suivante pour créer un pointeur au fichier nommémyfile.properties
, qui contiendra les propriétés attachées au nouveau rôle.Note
Le fichier de propriétés nouvellement créées n'est pas géré par les scriptsadd-user.sh
etadd-user.bat
inclus. Il devra être administré en externe.Pour une instance de domaine, utiliser la commande suivante :/host=master/core-service=management/security-realm=MyDomainRealm/authentication=properties:add(path=myfile.properties)
Pour une instance autonome, utiliser la commande suivante :/core-service=management/security-realm=MyDomainRealm/authentication=properties:add(path=myfile.properties)
Votre nouveau domaine de sécurité sera créé. Lorsque vous ajoutez des utilisateurs et des rôles à ce nouveau domaine, l'information va être stockée dans un fichier séparé des domaines de sécurité par défaut. Vous pouvez gérer ce nouveau fichier à l'aide de vos propres applications ou procédures.
17.3.3. Ajout d'un utilisateur à un domaine de sécurité
Éxécuter la commande
add-user.sh
ouadd-user.bat
.Ouvrez un terminal (shell) et changez de répertoireEAP_HOME/bin/
. Si vous exécutez Red Hat Enterprise Linux ou un autre système d'exploitation style UNIX, exécutezadd-user.sh
. Si vous exécutez sur un serveur Microsoft Windows, exécutezadd-user.bat
.Choisir d'ajouter un utilisateur de gestion ou un utilisateur d'application.
Pour cette procédure, saisirb
pour ajouter un utilisateur d'application.Choisir le domaine dans lequel l'utilisateur sera ajouté.
Par défaut, le seul domaine disponible estApplicationRealm
. Si vous avez ajouté un domaine personnalisé, vous pouvez saisir son nom à la place.Saisir le nom d'utilisateur, le mot de passe et les rôles lorsque vous y serez invité.
Saisir le nom d'utilisateur, le mot de passe et les rôles lorsque vous y serez invité. Vérifiez votre choix en tapantyes
, ouno
pour annuler les changements. Les changements sont inscrits dans les fichiers de propriétés du domaine de sécurité.
17.4. Sécurité des applications EJB
17.4.1. Identité Sécurité
17.4.1.1. L'identité de sécurité EJB
17.4.1.2. Définir l'identité de sécurité d'un EJB
<security-identity>
dans la configuration de la sécurité.
<security-identity>
n'est présente - l'identité de l'appelant de l'EJB sera utilisée.
Exemple 17.5. Définir l'identité de sécurité d'un EJB pour que ce soit la même que celle de l'appelant
<security-identity>
.
<ejb-jar> <enterprise-beans> <session> <ejb-name>ASessionBean</ejb-name> <!-- ... --> <security-identity> <use-caller-identity/> </security-identity> </session> <!-- ... --> </enterprise-beans> </ejb-jar>
Exemple 17.6. Définir l'idendité de sécurité d'un EJB à un rôle spécifique
<run-as>
et les balises <role-name>
dans la balise <security-identity>
.
<ejb-jar> <enterprise-beans> <session> <ejb-name>RunAsBean</ejb-name> <!-- ... --> <security-identity> <run-as> <description>A private internal role</description> <role-name>InternalRole</role-name> </run-as> </security-identity> </session> </enterprise-beans> <!-- ... --> </ejb-jar>
<run-as>
, un principal nommé anonymous
est assigné aux appels sortants. Pour assigner un autre principal, utiliser <run-as-principal>
.
<session> <ejb-name>RunAsBean</ejb-name> <security-identity> <run-as-principal>internal</run-as-principal> </security-identity> </session>
Note
<run-as>
et <run-as-principal>
à l'intérieur d'un élément de servlet.
Voir également :
17.4.2. Permissions de méthodes EJB
17.4.2.1. Permissions de méthodes EJB
<method-permisison>
définit les rôles qui peuvent appeler des méthodes de l'interface EJB. Vous pouvez définir des permissions pour les combinaisons suivantes :
- Toutes les méthodes d'interface de composant ou d'accueil de l'EJB nommé
- Une méthode spécifiée d'interface de composant ou d'accueil de l'EJB nommé
- Une méthode spécifiée à l'intérieur d'un ensemble de méthodes avec un nom surchargé
17.4.2.2. Utilisation des permissions de méthodes EJB
L'élément <method-permission>
définit les roles logiques qui peuvent accéder aux méthodes EJB définies par les éléments <method>
. Un certain nombre d'exemples expliquent la syntaxe XML. Plusieurs énoncés de method-permission peuvent être présents, et avoir un effet cumulatif. L'élément <method-permission>
est un dépendant de l'élément <assembly-descriptor>
du descripteur <ejb-jar>
.
Exemple 17.7. Permet aux rôles d'accéder à toutes les méthodes d'un EJB
<method-permission> <description>The employee and temp-employee roles may access any method of the EmployeeService bean </description> <role-name>employee</role-name> <role-name>temp-employee</role-name> <method> <ejb-name>EmployeeService</ejb-name> <method-name>*</method-name> </method> </method-permission>
Exemple 17.8. Permet aux rôles d'accéder uniquement à des méthodes spécifiques d'un EJB, et de déterminer quels paramètres de méthode peuvent être passés.
<method-permission> <description>The employee role may access the findByPrimaryKey, getEmployeeInfo, and the updateEmployeeInfo(String) method of the AcmePayroll bean </description> <role-name>employee</role-name> <method> <ejb-name>AcmePayroll</ejb-name> <method-name>findByPrimaryKey</method-name> </method> <method> <ejb-name>AcmePayroll</ejb-name> <method-name>getEmployeeInfo</method-name> </method> <method> <ejb-name>AcmePayroll</ejb-name> <method-name>updateEmployeeInfo</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> </method-permission>
Exemple 17.9. Permet à n'importe quel utilisateur authentifié d'accéder aux méthodes des EJB
<unchecked/>
permet à un utilisateur authentifié d'utiliser les méthodes spécifiées.
<method-permission> <description>Any authenticated user may access any method of the EmployeeServiceHelp bean</description> <unchecked/> <method> <ejb-name>EmployeeServiceHelp</ejb-name> <method-name>*</method-name> </method> </method-permission>
Exemple 17.10. Exclut totalement l'utilisation de certaines méthodes EJB
<exclude-list> <description>No fireTheCTO methods of the EmployeeFiring bean may be used in this deployment</description> <method> <ejb-name>EmployeeFiring</ejb-name> <method-name>fireTheCTO</method-name> </method> </exclude-list>
Exemple 17.11. Un <assembly-descriptor>
complet contenant plusieurs blocs de <method-permission>
<ejb-jar> <assembly-descriptor> <method-permission> <description>The employee and temp-employee roles may access any method of the EmployeeService bean </description> <role-name>employee</role-name> <role-name>temp-employee</role-name> <method> <ejb-name>EmployeeService</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <description>The employee role may access the findByPrimaryKey, getEmployeeInfo, and the updateEmployeeInfo(String) method of the AcmePayroll bean </description> <role-name>employee</role-name> <method> <ejb-name>AcmePayroll</ejb-name> <method-name>findByPrimaryKey</method-name> </method> <method> <ejb-name>AcmePayroll</ejb-name> <method-name>getEmployeeInfo</method-name> </method> <method> <ejb-name>AcmePayroll</ejb-name> <method-name>updateEmployeeInfo</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> </method-permission> <method-permission> <description>The admin role may access any method of the EmployeeServiceAdmin bean </description> <role-name>admin</role-name> <method> <ejb-name>EmployeeServiceAdmin</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <description>Any authenticated user may access any method of the EmployeeServiceHelp bean</description> <unchecked/> <method> <ejb-name>EmployeeServiceHelp</ejb-name> <method-name>*</method-name> </method> </method-permission> <exclude-list> <description>No fireTheCTO methods of the EmployeeFiring bean may be used in this deployment</description> <method> <ejb-name>EmployeeFiring</ejb-name> <method-name>fireTheCTO</method-name> </method> </exclude-list> </assembly-descriptor> </ejb-jar>
17.4.3. Annotations de sécurité EJB
17.4.3.1. Les annotations de sécurité EJB
javax.annotation.security
sont définies dans JSR250.
- @DeclareRoles
- Déclare quels rôles sont disponibles.
- @RunAs
- Configure l'identification de sécurité propagée d'un composant.
17.4.3.2. Utilisation des annotations de sécurité EJB
Vous pouvez utiliser deux descripteurs XML ou des annotations pour contrôler quels rôles de sécurité sont en mesure d'appeler des méthodes dans votre Enterprise JavaBeans (EJB). Pour plus d'informations sur l'utilisation des descripteurs XML, reportez-vous à Section 17.4.2.2, « Utilisation des permissions de méthodes EJB ».
Annotations pour contrôler les permissions de sécurité des EJB
- @DeclareRoles
- Utiliser @DeclareRoles pour définir dans quels rôles vérifier les permissions. Si aucun @DeclareRoles n'est présent, la liste sera créée automatiquement avec l'annotation @RolesAllowed. Pour plus d'informations sur la configuration des rôles, voir Java EE 6 Tutorial Specifying Authorized Users by Declaring Security Roles.
- @RolesAllowed, @PermitAll, @DenyAll
- Utiliser
@RolesAllowed
pour lister les rôles qui peuvent accéder à une méthode ou à des méthodes. Utiliser@PermitAll
ou@DenyAll
pour autoriser ou empêcher à des rôles d'utiliser une méthode ou des méthodes. Pour plus d'informations sur la configuration des permissions de méthode d'annotation, consultez Java EE 6 Tutorial Specifying Authorized Users by Declaring Security Roles. - @RunAs
- Utiliser
@RunAs
pour spécifier un rôle utilisé par une méthode quand elle lance des appels à partir d'une méthode annotée. Pour plus d'informations sur la façon de configurer les identités de sécurité propagées par des annotations, voir Java EE 6 Tutorial Propagating a Security Identity (Run-As).
Exemple 17.12. Exemple d'annotations de sécurité
@Stateless @RolesAllowed({"admin"}) @SecurityDomain("other") public class WelcomeEJB implements Welcome { @PermitAll public String WelcomeEveryone(String msg) { return "Welcome to " + msg; } @RunAs("tempemployee") public String GoodBye(String msg) { return "Goodbye, " + msg; } public String GoodbyeAdmin(String msg) { return "See you later, " + msg; } }
WelcomeEveryone
. La méthode GoodBye
exécute sous la forme du rôle tempemployee
pour les appels. Seul le rôle admin
peut accéder à la méthode GoodbyeAdmin
, ou à toute autre méthode sans annotation de sécurité.
17.4.4. Accès à distance aux EJB
17.4.4.1. JBoss Remoting (accès à distance)
Types de services pris en charge
- Socket / Secure Socket
- RMI / RMI sur SSL
- HTTP / HTTPS
- Servlet / Secure Servlet
- Bisocket / Secure Bisocket
Avertissement
Le système Remoting d'accès distant fournit également des services de marshalling et unmarshalling de données. Le marshaling de données désigne la capacité de déplacer en toute sécurité des données au-delà des limites de réseau et de la plate-forme, afin qu'un système séparé puisse effectuer des tâches dessus. Le travail est ensuite renvoyé vers le système d'origine et se comporte comme s'il avait eu lieu localement.
Lorsque vous créez une application cliente qui utilise Remoting, vous indiquez à votre application de communiquer avec le serveur en la configurant pour qu'elle utilise un type spécial de localisateur de ressources appelé un InvokerLocator
, qui est une chaîne simple avec un format de type URL. Le serveur écoute les requêtes des ressources distantes sur un connecteur
, qui est configuré comme faisant partie du sous-système remoting
. Le connecteur
transmet la demande à un ServerInvocationHandler
configuré. Chaque ServerInvocationHandler
implémente une méthode invoke(InvocationRequest)
qui sait comment gérer la demande.
Couches de framework JBoss Remoting
- L'utilisateur interagit avec la couche externe. Côté client, la couche externe est la classe
Client
, qui envoie des requêtes d'invocation. Côté serveur, c'est InvocationHandler, mis en œuvre par l'utilisateur, qui reçoit des demandes d'invocation. - Le transport est contrôlé par la couche d'invocateur.
- La couche inférieure contient le marshaller et le unmarshaller, qui convertit les formats de données en formats de transmission.
17.4.4.2. Remoting Callbacks
InvocationRequest
au client. Votre code côté serveur fonctionne de la même façon que le rappel soit synchrone ou asynchrone. Seul le client a besoin de connaître la différence. InvocationRequest du serveur envoie un responseObject
au client. Il s'agit de la charge que le client a demandée. C'est peut-être une réponse directe à une demande ou à une notification d'événement.
m_listeners
. Il contient une liste de tous les listeners qui ont été ajoutés à votre server handler. L'interface du ServerInvocationHandler
inclut des méthodes qui vous permettent de gérer cette liste.
org.jboss.remoting.InvokerCallbackHandler
, qui traite les données de callback. Après l'implémentation du gestionnaire de callbacks, soit vous vous ajoutez vous-même, en tant que listener de «pull callback», ou bien, vous installez un serveur de callbacks pour un «push callback».
Pour un «pull callback», votre client s'ajoute à la liste du serveur des listeners à l'aide de la méthode Client.addListener()
. Ensuite, il interroge le serveur périodiquement au sujet de l'exécution synchrone des données de callback. Ce sondage est effectué à l'aide de Client.getCallbacks()
.
Un «push callback» requiert que votre application cliente exécute elle-même son propre InvocationHandler. Pour ce faire, vous devez exécuter un service Remoting sur le client lui-même. Ceci s'appelle un callback server. Le server de callbacks accepte les requêtes entrantes de façon asynchrone et les traite pour l'auteur de la demande (dans ce cas, le serveur). Pour inscrire le serveur de callbacks de votre client avec le serveur principal, passez l'argument InvokerLocator
du serveur de callbacks comme deuxième argument à la méthode addListener
.
17.4.4.3. Remoting Server Detection
17.4.4.4. Configurer le sous-système de JBoss Remoting
JBoss Remoting a trois éléments configurables de niveau supérieur : le pool de worker threads, un ou plusieurs connecteurs et une série de liens URI locaux et distants. Cette rubrique propose une explication pour chaque élément configurable, des exemples de commandes CLI pour savoir comment configurer chaque élément et un exemple XML d'un sous-système entièrement configuré. Cette configuration s'applique uniquement au serveur. La plupart des gens n'auront pas à configurer le sous-système de communication à distance, sauf s'ils utilisent des connecteurs personnalisés pour leurs propres applications. Les applications qui agissent comme des clients Remoting, comme les EJB, nécessitent une configuration distincte pour se connecter à un connecteur spécifique.
Note
Les commandes CLI sont formulées pour un domaine géré, lorsque vous configurez le profil par défaut
. Pour configurer un profil différent, changez-en le nom. Pour un serveur autonome, omettre la section /profile=default
de la commande.
Il y a un certain nombre d'aspects de configuration qui sont en dehors du sous-système remoting
:
- Network Interface
- L'interface de réseau qui est utilisée par le sous-système
remoting
est l'interfacepublique
définie dansdomain/configuration/domain.xml
ou dansstandalone/configuration/standalone.xml
.<interfaces> <interface name="management"/> <interface name="public"/> <interface name="unsecure"/> </interfaces>
La définition par-hôte de l'interfacepublique
est définie danshost.xml
dans le même répertoire quedomain.xml
oustandalone.xml
. Cette interface est également utilisée par plusieurs autres sous-systèmes. Soyez vigilants quand vous la modifierez.<interfaces> <interface name="management"> <inet-address value="${jboss.bind.address.management:127.0.0.1}"/> </interface> <interface name="public"> <inet-address value="${jboss.bind.address:127.0.0.1}"/> </interface> <interface name="unsecure"> <!-- Used for IIOP sockets in the standard configuration. To secure JacORB you need to setup SSL --> <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/> </interface> </interfaces>
- socket-binding
- La liaison-socket par défaut utilisée par le sous-système
remoting
se lie au port TCP 4447. Reportez-vous à la documentation sur les liaisons de socket et de groupes de liaisons de socket pour plus d'informations si vous avez besoin de procéder à une modification. - Remoting Connector Reference pour EJB
- Le sous-système EJB contient une référence vers le connecteur à distance pour les invocations de méthodes à distance. Voici la configuration par défaut :
<remote connector-ref="remoting-connector" thread-pool-name="default"/>
- Configuration du transport sécurisé
- Les transports à distance (Remoting) utilisent StartTLS pour avoir une connexion sécurisée (HTTPS, Secure Servlet, etc.) si le client le demande. La même liaison de socket (port réseau) est utilisée pour les connexions sécurisées et non sécurisées, donc aucune configuration côté serveur supplémentaire n'est nécessaire. Le client demande le transport sécurisé ou non sécurisé, tel que le dictent ses besoins. Les composants JBoss EAP 6 qui utilisent Remoting, tels que les fournisseur JMS, EJB et ORB exigent des interfaces sécurisées par défaut.
Avertissement
Un Worker Thread Pool est un ensemble de threads qui peuvent traiter les tâches qui arrivent par les connecteurs Remoting. Il s'agit d'un seul élément <worker-thread-pool>
, et nécessite un certain nombre d'attributs. Régler ces attributs si vous avez des timeouts de réseau, ou si vous devez limiter l'utilisation de la mémoire. Les conseils varient suivant la situation dans laquelle vous vous trouvez. Contacter Red Hat Global Support Services pour obtenir davantage d'informations.
Tableau 17.2. Attributs de Worker Thread Pool
Attribut | Description | CLI Command |
---|---|---|
read-threads |
Le nombre de read-threads à créer pour le worker à distance. La valeur par défaut est
1 .
| /profile=default/subsystem=remoting/:write-attribute(name=worker-read-threads,value=1)
|
write-threads |
Le nombre de write-threads à créer pour le worker à distance. La valeur par défaut est
1 .
| /profile=default/subsystem=remoting/:write-attribute(name=worker-write-threads,value=1)
|
task-keepalive |
Le nombre de millisecondes pour conserver les threads de tâche de workers à distance non-core vivants. La valeur par défaut est
60 .
| /profile=default/subsystem=remoting/:write-attribute(name=worker-task-keepalive,value=60)
|
task-max-threads |
Le nombre de maximum de threads pour le Worker Task Thread Pool distant. La valeur par défaut est
60 .
| /profile=default/subsystem=remoting/:write-attribute(name=worker-task-max-threads,value=16)
|
task-core-threads |
Le nombre de threads principaux pour le Worker Task Thread Pool distant. La valeur par défaut est
4 .
| /profile=default/subsystem=remoting/:write-attribute(name=worker-task-core-threads,value=4)
|
task-limit |
Le nombre de maximum de tâches de worker distantes. La valeur par défaut est
16384 .
| /profile=default/subsystem=remoting/:write-attribute(name=worker-task-limit,value=16384)
|
Le connecteur est le principal élément de configuration de Remoting (d'accès à distance). Les connecteurs multiples sont autorisés. Chacun se compose d'un élément <connector>
avec plusieurs sous-éléments, ainsi que quelques attributs possibles. Le connecteur par défaut est utilisé par plusieurs sous-systèmes de JBoss EAP 6. Des paramètres spécifiques pour les éléments et les attributs de vos connecteurs personnalisés dépendent de vos applications, donc contactez Red Hat Global Support Services pour plus d'informations.
Tableau 17.3. Attributs de connecteur
Attribut | Description | CLI Command |
---|---|---|
socket-binding | Le nom de la liaison de socket à utiliser pour ce connecteur. | /profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=socket-binding,value=remoting)
|
authentication-provider |
Le module JASPIC (de l'anglais Java Authentication Service Provider Interface for Containers) à utiliser avec ce connecteur. Le module doit être dans le chemin de classes.
| /profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=authentication-provider,value=myProvider)
|
security-realm |
En option. Le domaine de la sécurité qui contient les utilisateurs, mots de passe et les rôles de votre application. Un EJB ou une Application Web peut authentifier sur un domaine de sécurité.
ApplicationRealm est disponible dans une installation de JBoss EAP 6 par défaut.
| /profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=security-realm,value=ApplicationRealm)
|
Tableau 17.4. Éléments de connecteur
Attribut | Description | CLI Command |
---|---|---|
sasl |
Élément englobant des mécanismes d'authentification SASL (Simple Authentication and Security Layer)
| N/A
|
propriétés |
Contient un ou plusieurs éléments
<property> , contenant chacun un attribut name et un attribut value optionnel.
| /profile=default/subsystem=remoting/connector=remoting-connector/property=myProp/:add(value=myPropValue)
|
Vous pouvez spécifier trois types différents d'attribut de connexion sortante :
- Connexion sortante vers un URI.
- Connexion sortante locale – se connectant à une ressource locale, comme un socket.
- Connexion sortante à distance – se connectant à une ressource à distance et s'authentifiant par l'intermédiaire d'un domaine de sécurité.
<outbound-connections>
. Chacun de ces types de connexion prend un attribut outbound-socket-binding-ref
. La connexion sortante prend un attribut uri
. La connexion sortante prend les attributs facultatifs username
(nom d'utilisateur) et security-realm
(domaine de sécurité) pour l'autorisation.
Tableau 17.5. Éléments de connexion sortante
Attribut | Description | CLI Command |
---|---|---|
outbound-connection | Connexion sortante standard | /profile=default/subsystem=remoting/outbound-connection=my-connection/:add(uri=http://my-connection)
|
local-outbound-connection | Connexion sortante en schéma implicite local:// URI. | /profile=default/subsystem=remoting/local-outbound-connection=my-connection/:add(outbound-socket-binding-ref=remoting2)
|
remote-outbound-connection |
Connexion sortante en schéma remote:// URI, utilisant l'authentification de base/digest avec un domaine de sécurité.
| /profile=default/subsystem=remoting/remote-outbound-connection=my-connection/:add(outbound-socket-binding-ref=remoting,username=myUser,security-realm=ApplicationRealm)
|
Avant de définir des éléments enfants SASL, vous devez créer l'élément SASL initial. Utiliser la commande suivante :
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:add
Tableau 17.6. Éléments enfants SASL
Attribut | Description | CLI Command |
---|---|---|
include-mechanisms |
Contient un attribut
value , qui correspond à une liste de mécanismes SASL.
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=include-mechanisms,value=["DIGEST","PLAIN","GSSAPI"]) |
qop |
Contient un attribut
value , qui correspond à une liste de valeurs de protection SASL, en ordre décroissant de préférence.
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=qop,value=["auth"]) |
puissance |
Contient un attribut
value , qui correspond à une liste de valeurs de puissance cipher SASL, en ordre décroissant de préférence.
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=strength,value=["medium"]) |
reuse-session |
Contient un attribut
value , qui correspond à une valeur booléenne. Si sur true, tente de réutiliser les sessions.
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=reuse-session,value=false) |
server-auth |
Contient un attribut
value , qui correspond à une valeur booléenne. Si sur true, le serveur s'authentifie auprès du client.
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=server-auth,value=false) |
politique |
Un élément clôturé qui contient zéro ou plusieurs des éléments suivants, prenant chacun une seule
valeur .
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:add /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=forward-secrecy,value=true) /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-active,value=false) /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-anonymous,value=false) /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-dictionary,value=true) /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-plain-text,value=false) /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=pass-credentials,value=true) |
propriétés |
Contient un ou plusieurs éléments
<property> , contenant chacun un attribut name et un attribut value optionnel.
|
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/property=myprop:add(value=1) /profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/property=myprop2:add(value=2) |
Exemple 17.13. Exemples de configurations
<subsystem xmlns="urn:jboss:domain:remoting:1.1"> <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"/> </subsystem>
<subsystem xmlns="urn:jboss:domain:remoting:1.1"> <worker-thread-pool read-threads="1" task-keepalive="60" task-max-threads="16" task-core-thread="4" task-limit="16384" write-threads="1" /> <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"> <sasl> <include-mechanisms value="GSSAPI PLAIN DIGEST-MD5" /> <qop value="auth" /> <strength value="medium" /> <reuse-session value="false" /> <server-auth value="false" /> <policy> <forward-secrecy value="true" /> <no-active value="false" /> <no-anonymous value="false" /> <no-dictionary value="true" /> <no-plain-text value="false" /> <pass-credentials value="true" /> </policy> <properties> <property name="myprop1" value="1" /> <property name="myprop2" value="2" /> </properties> </sasl> <authentication-provider name="myprovider" /> <properties> <property name="myprop3" value="propValue" /> </properties> </connector> <outbound-connections> <outbound-connection name="my-outbound-connection" uri="http://myhost:7777/"/> <remote-outbound-connection name="my-remote-connection" outbound-socket-binding-ref="my-remote-socket" username="myUser" security-realm="ApplicationRealm"/> <local-outbound-connection name="myLocalConnection" outbound-socket-binding-ref="my-outbound-socket"/> </outbound-connections> </subsystem>
Aspects de la configuration non encore documentés
- JNDI et Détection automatique multi diffusion
17.4.4.5. Utilisation des domaines de sécurité avec les clients EJB distants
- Ajoutez un nouveau domaine de sécurité au contrôleur de domaine ou au serveur autonome.
- Ajoutez les paramètres suivants au fichier
jboss-ejb-client.properties
, qui est dans le chemin de classes de l'application. Cet exemple suppose que la connexion est appeléepar défaut
par les autres paramètres qui se trouvent dans le fichier.remote.connection.default.username=appuser remote.connection.default.password=apppassword
- Créez un connecteur Remoting personnalisé sur le domaine ou sur le serveur autonome qui utilise le nouveau domaine de sécurité.
- Déployez votre EJB dans le groupe de serveur configuré pour utiliser le profil avec le connecteur Remoting personnalisé, ou dans le serveur autonome si vous n'utilisez pas de domaine géré.
17.4.4.6. Ajout d'un domaine de sécurité
Exécuter l'interface CLI
Démarrez par la commandejboss-cli.sh
oujboss-cli.bat
et connectez-vous au serveur.Créer le nouveau domaine de sécurité lui-même.
Exécutez la commande suivante pour créer un nouveau domaine de sécurité nomméMyDomainRealm
sur un contrôleur de domaine ou sur un serveur autonome.Pour une instance de domaine, utiliser la commande suivante :/host=master/core-service=management/security-realm=MyDomainRealm:add()
Pour une instance autonome, utiliser la commande suivante :/core-service=management/security-realm=MyDomainRealm:add()
Créer les références du fichier de propriétés qui stocke les informations sur le nouveau rôle.
Exécutez la commande suivante pour créer un pointeur au fichier nommémyfile.properties
, qui contiendra les propriétés attachées au nouveau rôle.Note
Le fichier de propriétés nouvellement créées n'est pas géré par les scriptsadd-user.sh
etadd-user.bat
inclus. Il devra être administré en externe.Pour une instance de domaine, utiliser la commande suivante :/host=master/core-service=management/security-realm=MyDomainRealm/authentication=properties:add(path=myfile.properties)
Pour une instance autonome, utiliser la commande suivante :/core-service=management/security-realm=MyDomainRealm/authentication=properties:add(path=myfile.properties)
Votre nouveau domaine de sécurité sera créé. Lorsque vous ajoutez des utilisateurs et des rôles à ce nouveau domaine, l'information va être stockée dans un fichier séparé des domaines de sécurité par défaut. Vous pouvez gérer ce nouveau fichier à l'aide de vos propres applications ou procédures.
17.4.4.7. Ajout d'un utilisateur à un domaine de sécurité
Éxécuter la commande
add-user.sh
ouadd-user.bat
.Ouvrez un terminal (shell) et changez de répertoireEAP_HOME/bin/
. Si vous exécutez Red Hat Enterprise Linux ou un autre système d'exploitation style UNIX, exécutezadd-user.sh
. Si vous exécutez sur un serveur Microsoft Windows, exécutezadd-user.bat
.Choisir d'ajouter un utilisateur de gestion ou un utilisateur d'application.
Pour cette procédure, saisirb
pour ajouter un utilisateur d'application.Choisir le domaine dans lequel l'utilisateur sera ajouté.
Par défaut, le seul domaine disponible estApplicationRealm
. Si vous avez ajouté un domaine personnalisé, vous pouvez saisir son nom à la place.Saisir le nom d'utilisateur, le mot de passe et les rôles lorsque vous y serez invité.
Saisir le nom d'utilisateur, le mot de passe et les rôles lorsque vous y serez invité. Vérifiez votre choix en tapantyes
, ouno
pour annuler les changements. Les changements sont inscrits dans les fichiers de propriétés du domaine de sécurité.
17.4.4.8. Accès EJB à distance utilisant le cryptage SSL
Avertissement
17.5. Sécurité des applications JAX-RS
17.5.1. Activez la sécurité basée-rôle pour RESTEasy JAX-RS Web Service
RESTEasy supporte les annotations @RolesAllowed, @PermitAll, et @DenyAll sur les méthodes JAX-RS. Cependant, il ne reconnaît pas ces annotations par défaut. Suivre les étapes suivantes pour configurer le fichier web.xml
et pour activer la sécurité basée-rôle.
Avertissement
Procédure 17.3. Activez la sécurité basée-rôle pour RESTEasy JAX-RS Web Service
- Ouvrir le fichier
web.xml
de l'application dans un éditeur de texte. - Ajoutez le <context-param> suivant au fichier, dans les balises
web-app
:<context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param>
- Déclarez tous les rôles utilisés dans le fichier RESTEasy JAX-RS WAR file, en utilisant les balises de <security-role> :
<security-role> <role-name>ROLE_NAME</role-name> </security-role> <security-role> <role-name>ROLE_NAME</role-name> </security-role>
- Autorisez l'accès à tous les URL gérés par le runtime JAX-RS pour tous les rôles :
<security-constraint> <web-resource-collection> <web-resource-name>Resteasy</web-resource-name> <url-pattern>/PATH</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ROLE_NAME</role-name> <role-name>ROLE_NAME</role-name> </auth-constraint> </security-constraint>
La sécurité basée rôle à été activée dans l'application, avec un certain nombre de rôles définis.
Exemple 17.14. Exemple de configuration de sécurité basée rôles
<web-app> <context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <security-constraint> <web-resource-collection> <web-resource-name>Resteasy</web-resource-name> <url-pattern>/security</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>user</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>user</role-name> </security-role> </web-app>
17.5.2. Sécuriser un service JAX-RS Web par des annotations
Cette rubrique couvre les étapes à parcourir pour sécuriser un service JAX-RS Web par les annotations de sécurité supportées.
Procédure 17.4. Sécuriser un service JAX-RS Web par des annotations de sécurité supportées.
- Activez la sécurité basée-rôle. Pour plus d'informations, voir Section 17.5.1, « Activez la sécurité basée-rôle pour RESTEasy JAX-RS Web Service ».
- Ajoutez des annotations de sécurité au service JAX-RS Web. RESTEasy supporte les annotations suivantes :
- @RolesAllowed
- Définit les rôles qui peuvent accéder à la méthode. Tous les rôles doivent être définis dans le fichier
web.xml
. - @PermitAll
- Autorise tous les rôles définis dans le fichier
web.xml
à accéder à la méthode. - @DenyAll
- Refuse tout accès à la méthode.
17.6. Protocole mots de passes distants sécurisés
17.6.1. Protocole pour mots de passes distants sécurisés (SRP pour Secure Remote Password)
Ce document décrit un mécanisme d'authentification de réseau à cryptage puissant connu comme le nom de protocole SRP (Secure Remote Password). Ce mécanisme est approprié pour négocier des connexions sécurisées avec un mot de passe fourni par l'utilisateur, tout en éliminant les problèmes de sécurité traditionnellement associés à des mots de passe réutilisables. Ce système effectue également un échange de clés sécurisée dans le processus d'authentification, ce qui permet à ce que des couches de sécurité (protection de la vie privée et/ou de l'intégrité) soient activées lors de la session. Il n'est nul besoin de serveurs de clés de confiance ou d'infrastructures de certificats, et les clients ne sont pas tenus de stocker ou de gérer des clés à long terme. SRP offre des avantages tant au niveau de la sécurité qu'au niveau déploiement sur les techniques existantes de stimulation / réponse, ce qui en fait un remplacement idéal pour les besoins d'authentification de mots de passe sécurisés.
17.6.2. Configuration du protocole SRP (Secure Remote Password)
SRPVerifierStore
. Vous trouverez des informations sur l'implémentation à Implémentation SRPVerifierStore.
Procédure 17.5. Intégrer le store de mots de passe existant
Créer un store d'informations de mots de passe hachés.
Si vos mots de passe sont déjà stockés sous une forme hachée irréversible, vous aurez besoin de faire cela sur la base utilisateur.Vous pouvez implémenter unsetUserVerifier(String, VerifierInfo)
en tant que méthode noOp, ou d'une méthode qui envoie une exception signifiant que le store est en lecture-seule.Créer l'interface SRPVerifierStore.
Créer une implémentation d'interfaceSRPVerifierStore
personnalisée qui puisse obtenirVerifierInfo
du store que vous avez créé.LeverifyUserChallenge(String, Object)
peut servir à intégrer des schémas basés token de matériel existant comme SafeWord ou Radius dans l'algorithme SRP. Cette méthode d'interface est appelée uniquement lorsque la configuration SRPLoginModule du client spécifie l'option hasAuxChallenge.Créer le MBean JNDI.
Créer un MBean qui expose l'interfaceSRPVerifierStore
disponible à JNDI, et expose tous les paramètres configurables requis.Le serviceorg.jboss.security.srp.SRPVerifierStoreService
par défaut vous permet d'implémenter ceci. Vous pouvez également implémenter le MBean par une implémentation de fichier de propriétés Java duSRPVerifierStore
.
L'implémentation par défaut de l'interface SRPVerifierStore
n'est pas recommandée pour les systèmes de production, car elle exige que toutes les informations de hachage de mot de passe soient disponibles sous forme de fichier d'objets sérialisés.
SRPVerifierStore
fournit un accès à l'objet SRPVerifierStore.VerifierInfo
pour un nom d'utilisateur donné. La méthode getUserVerifier(String)
est appelée par le SRPService au départ d'une session SRP utilisateur pour obtenir les paramètres requis par l'algorithme SRP.
Éléments d'un Objet VerifierInfo
- nom d'utilisateur
- Le nom d'utilisateur ou l'ID utilisateur pour s'authentifier
- verifier
- Un hachage unidirectionnel du mot de passe que l'utilisateur saisit comme preuve d'identité. La classe
org.jboss.security.Util
inclut une méthodecalculateVerifier
qui exécute l'algorithme de hachage de mot de passe. Le mot de passe de sortie prend la formeH(salt | H(username | password))
, oùH
est la fonction de hachage sécurisée SHA, telle que définie par RFC2945. Le nom d'utilisateur est converti d'une chaîne en un byte [] en utilisant le codage UTF-8. - salt
- Un nombre aléatoire utilisé pour augmenter la difficulté d'une attaque de force brute de dictionnaire sur la base de données de mot de passe de vérification au cas où la base de données soit compromise. La valeur doit être générée à partir d'un algorithme de cryptage fort composé de nombres aléatoires lorsque le mot de passe en texte clair existant de l'utilisateur est haché.
- g
- Le générateur primitif d'algorithme SRP. Cela peut être un paramètre fixe bien connu plutôt qu'un paramètre par utilisateur. La classe d'utilitaire de
org.jboss.security.srp.SRPConf
fournit plusieurs paramètres pourg
, avec une valeur par défaut appropriée obtenue par l'intermédiaire deSRPConf.getDefaultParams().g()
. - N
- SRP algorithm safe-prime modulus. Cela peut être un paramètre fixe bien connu, plutôt qu'un paramètre par utilisateur. La classe d'utilitaire de
org.jboss.security.srp.SRPConf
fournit plusieurs paramètres pour «N», y compris un bon nombre de valeurs par défaut obtenues viaSRPConf.getDefaultParams().N()
.
Exemple 17.15. L'interface SRPVerifierStore
package org.jboss.security.srp; import java.io.IOException; import java.io.Serializable; import java.security.KeyException; public interface SRPVerifierStore { public static class VerifierInfo implements Serializable { public String username; public byte[] salt; public byte[] g; public byte[] N; } public VerifierInfo getUserVerifier(String username) throws KeyException, IOException; public void setUserVerifier(String username, VerifierInfo info) throws IOException; public void verifyUserChallenge(String username, Object auxChallenge) throws SecurityException; }
17.7. L'archivage sécurisé des mots de passe pour les strings de nature confidentielle
17.7.1. Système d'archivage sécurisé de mots de passe
17.7.2. Configurer et utiliser l'archivage sécurisé des mots de passe
Procédure 17.6. Étapes de base pour configurer et utiliser l'archivage sécurisé des mots de passe
- Installation d'un Java Keystore pour stocker des clés pour le cryptage de mots de passe.Pour plus d'informations sur la création d'un keystore, veuillez consulter Section 17.7.4, « Créer un keystore Java pour stocker des strings sensibles ».
- Initialiser l'archivage sécurisé des mots de passePour savoir comment masquer le mot de passe et initialiser la valeur de mot de passe, veuillez consulter Section 17.7.5, « Initialiser le Password Vault ».
- Configurer JBoss EAP pour qu'il utilise l'archivage sécurisé des mots de passePour obtenir des informations sur la façon de configurer EAP 6 afin qu'il puisse utiliser le Password Vault, voir Section 17.7.6, « Configurer JBoss EAP pour qu'il utilise l'archivage sécurisé des mots de passe ».
- Stocker une chaîne sensible dans l'archivage sécurisé des mots de passe.Pour obtenir des informations sur la façon de stocker une chaîne sensible dans le Password Vault, voir Section 17.7.8, « Stocker une chaîne sensible dans l'archivage sécurisé des mots de passe ».
- Configurer JBoss EAP pour qu'il utilise l'archivage sécurisé des mots de passePour obtenir des informations sur la façon de configurer EAP 6 afin qu'il puisse utiliser le Password Vault, voir Section 17.7.6, « Configurer JBoss EAP pour qu'il utilise l'archivage sécurisé des mots de passe ». Pour un implémentation personnalisée, voir Section 17.7.7, « Configurer JBoss EAP pour qu'il utilise une implémentation d'archivage sécurisé personnalisée ».
Note
Pour obtenir des informations sur la façon de chiffrer une chaîne sensible, voir Section 17.7.9, « Utiliser un string sensible crypté dans Configuration. ».Pour obtenir des informations sur la façon d'utiliser une chaîne sensible chiffrée, voir Section 17.7.10, « Utiliser un string sensible crypté dans une Application ».Pour vérifier une chaîne sensible dans le Password Vault, voir Section 17.7.11, « Vérifiez si une chaîne sensible se situe dans l'archivage sécurisé des mots de passe ».Pour supprimer une chaîne sensible du Password Vault, voir Section 17.7.12, « Supprimer une chaîne sensible de l'archivage sécurisé des mots de passe ».
17.7.3. Obtenez le mot de passe Keystore d'une source externe
<vault-option name="KEYSTORE_PASSWORD" value="[here]"
{EXT}...
: fait référence à la commande exacte pour laquelle ‘…’ est la commande exacte. Exemmple :{EXT}/usr/bin/getmypassword --section 1 --query company
, exécutez la commande/usr/bin/getmypassword
qui affiche le mot de passe ou la sortie standard, et l'utilise comme mot de passe pour le keystore de l'archivage sécurisé. Dans cet exemple, la commande utilise deux options : --section 1 et --query company.{EXTC[:expiration_in_millis]}...
: fait référence à la commande exacte pour laquelle '...' est la ligne de commande précise qui est passée à la méthode Runtime.exec(String) pour exécuter une commande de plateforme. La première ligne de la sortie de commande est utilisée comme mot de passe. La variante EXTC met en cache les mots de passe pour expiration_in_millis milliseconds. L'expiration du cache par défaut est 0 (zéro), ce qui signifie que les éléments mis en cache n'expirent jamais. Exemple :{EXTC:120000}/usr/bin/getmypassword --section 1 --query company
vérifie si le cache contient une sortie/usr/bin/getmypassword
, et s'il contient la sortie, alors utilisez-la. S'il ne contient pas de sortie, exécutez la commande pour la faire aller dans le cache, et utilisez-la. Dans cet exemple, le cache expire dans 2 minutes (120000 millisecondes).{CMD}...
ou{CMDC[:expiration_in_millis]}...
: la commande générale est une chaîne délimitée par ',' où la première partie correspond à la commande et le reste correspond aux paramètres. La virgule peut être à la place une barre oblique de retour pour demeurrer dans le paramètre. Exemple,{CMD}/usr/bin/getmypassword,--section,1,--query,company
{CLASS[@jboss_module_spec]}classname[:ctorargs]
où [:ctorargs] est une chaîne optionnelle délimitée par ':' du nom de classe qui va être passé au nom de classe ctor. Le ctorargs lui-même correspond à une liste de chaînes délimitées par des virgules. Exemple,{CLASS@org.test.passwd}org.test.passwd.ExternamPassworProvider
. Dans cet exemple, nous chargeons la classeorg.test.passwd.ExternamPassworProvider
du moduleorg.test.passwd
et on utilise la méthodetoCharArray()
pour obtenir le mot de passe. SitoCharArray()
n'est pas disponible, utiliser la méthodetoString()
. La classeorg.test.passwd.ExternamPassworProvider
doit posséder le constructeur par défaut.
17.7.4. Créer un keystore Java pour stocker des strings sensibles
Conditions préalables
- La commande
keytool
fournie par le Java Runtime Environment (JRE). Chercher le chemin du fichier qui se trouve à l'emplacement suivant/usr/bin/keytool
dans Red Hat Enterprise Linux.
Avertissement
keytool
à l'aide du même fournisseur que le JDK que vous utilisez.
keytool
de JDK d'un fournisseur dans une instance JBoss EAP exécutant dans un JDK provenant d'un fournisseur différent, vous aurez l'exception suivante :
java.io.IOException: com.sun.crypto.provider.SealedObjectForKeyProtector
Procédure 17.7. Installation d'un Java keystore
Créez un répertoire pour stocker votre keystore et autres informations cryptées.
Créez un répertoire qui contiendra votre keystore et d'autres informations pertinentes. Le reste de cette procédure assume que le répertoire estEAP_HOME/vault/
. Comme le répertoire devra contenir des informations sensibles, il devra être accessible à un nombre restraint d'utilisateurs. Le compte d'utilisateur sous lequel JBoss EAP exécute requiert au minimum un accès en lecture-écriture.Déterminer les paramètres à utiliser avec la commande
keytool
.Choisissez les valeurs des paramètres suivants :- alias
- L'alias est un identificateur unique pour l'archivage sécurisé ou autres données stockées dans le keystore. Les alias sont insensibles à la casse.
- storetype
- Le storetype indique le type de keystore. La valeur
jceks
est conseillée. - keyalg
- L'algorithme à utiliser pour le cryptage. Consultez la documentation de votre JRE et de votre système d'exploitation pour étudier vos possibilités.
- keysize
- La taille d'une clé de cryptage impacte sur la difficulté de décrypter au seul moyen de la force brutale. Pour plus d'informations sur les valeurs appropriées, voir la documentation distribuée avec
keytool
. - storepass
- La valeur de
storepass
est le mot de passe utilisé pour authentifier le keystore, et pour que la clé puisse être lue. Le mot de passe doit contenir au moins 6 caractères de long et doit être fourni quand on accède au keystore. Si vous omettez ce paramètre, on vous demandera de le saisir quand vous exécuterez la commande. - keypass
- La valeur de
keypass
est le mot de passe utilisé pour accéder à la clé spécifique et doit correspondre à la valeur du paramètrestorepass
. - validité
- La valeur de
validité
est la durée (en jours) pendant laquelle la clé sera valide. - keystore
- La valeur du
keystore
correspond au chemin et nom du fichier dans lequel sont stockés les valeurs du keystore. Le fichier de clés est créé lorsque les données y sont ajoutées.Assurez-vous d'utiliser le séparateur de chemin d’accès correct du fichier:/
(barre oblique inverse) pour Red Hat Enterprise Linux et systèmes d'exploitation similaires, et\
pour les serveurs Microsoft Windows.
La commandekeytool
a plusieurs options. Consultez la documentation de votre JRE ou de votre système d'exploitation pour obtenir plus d'informations.Exécuter la commande
keytool
Lancer votre interface de ligne de commandes de votre système d'exploitation et exécuter la commandekeytool
, en fournissant les informations que vous avez collectées.
Exemple 17.16. Créer un Java keystore
$ keytool -genseckey -alias vault -storetype jceks -keyalg AES -keysize 128 -storepass vault22 -keypass vault22 -validity 730 -keystore EAP_HOME/vault/vault.keystore
Un keystore aura été créé dans le fichier EAP_HOME/vault/vault.keystore
. Il stocke une clé unique, dans l'archivage sécurisé
alias, qui sera utilisé pour stocker les strings cryptés, comme les mots de passe, dans JBoss EAP.
17.7.5. Initialiser le Password Vault
Conditions préalables
Le Password Vault peut être initialisé de manière interactive, avec une invite pour la valeur de chaque paramètre, ou de manière non-interactive, où vous fournissez les valeurs de tous les paramètres sur la ligne de commande. Chaque méthode donne le même résultat, vous pouvez donc choisir la méthode qui vous convient.
- URL du keystore (KEYSTORE_URL)
- Le chemin d'accès ou URI du fichier keystore. Les exemples utilisent
EAP_HOME/vault/
.vault.keystore
- Mot de passe du keystore (KEYSTORE_PASSWORD)
- Le mot de passe utilisé pour accéder au keystore.
- Salt (SALT)
- La valeur de
salt
correspond soit à une chaîne de huit caractères au hasard, utilisés avec le nombre d'itérations, pour chiffrer le contenu du keystore. - Alias de keystore Alias (KEYSTORE_ALIAS)
- L'alias sous lequel le keystore est connu.
- Nombre d'itérations (ITERATION_COUNT)
- Le nombre de fois que l'algorithme de chiffrement est exécuté.
- Fichiers de chiffrement du répertoire au store (ENC_FILE_DIR)
- Le chemin où les fichiers chiffrés sont stockés. Normalement, le répertoire contient l'archivage des mots de passe sécurisés.Il est pratique mais non pas obligatoire de stocker toutes vos informations de chiffrement au même endroit dans le keystore. Ce répertoire doit être accessible à des utilisateurs limités uniquement. Au minimum, le compte d'utilisateur sous lequel JBoss EAP exécute requiert un accès lecture-écriture. Si vous avez suivi Section 17.7.4, « Créer un keystore Java pour stocker des strings sensibles », votre keystore se trouvera dans le répertoire nommé
EAP_HOME/vault/
.Note
La barre oblique inverse finale ou oblique sur le nom du répertoire est nécessaire. Assurez-vous d'utiliser le séparateur de chemin d’accès correct du fichier: / (forward slash) for Red Hat Enterprise Linux and similar operating systems, \ (backslash) pour les serveurs Microsoft Windows. - Vault Block (VAULT_BLOCK)
- Nom donné à ce bloc dans l'archivage sécurisé des mots de passes. Choisissez une valeur qui signifie quelquechose pour vous.
- Attribut (ATTRIBUTE)
- Nom donné à l'attribut stocké. Choisissez une valeur qui signifie quelquechose pour vous. Par exemple, vous pouvez choisir un nom que vous associez à une source de données.
- Attribut de Sécurité (SEC-ATTR)
- Le mot de passe qui est stocké dans l'archivage sécurisé de mots de passe.
Procédure 17.8. Exécutez la commande d'archivage sécurisé des mots de passe de façon interactive
Exécutez la commande d'archivage sécurisé des mots de passe de façon interactive
Lancez l'interface en ligne de commande de votre système d'exploitation et exécutezEAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server). Démarrez une nouvelle session interactive en tapant0
(zéro).Complétez les paramètres à l'invite.
Suivez les instructions pour saisir les paramètres qui conviennent.Notez les informations de mot de passe masqué.
Le mot de passe masqué, salt, et le nombre d'itérations sont imprimés en sortie standard. Prenez-en note dans un endroit sûr. Ils sont requis pour ajouter des entrées dans l'archivage sécurisé. Si vous donnez accès au fichier de keystore, ses valeurs permettraient à un assailant d'accéder à des informations sensibles dans l'archivage sécurisé.Sortir de la console interactive.
Saisir3
(trois) pour sortir de la console interactive.
Exemple 17.17. Exécutez la commande d'archivage sécurisé des mots de passe de façon interactive
Please enter a Digit:: 0: Start Interactive Session 1: Remove Interactive Session 2: Exit 0 Starting an interactive session Enter directory to store encrypted files:EAP_HOME/vault/ Enter Keystore URL:EAP_HOME/vault/vault.keystore Enter Keystore password: vault22 Enter Keystore password again: vault22 Values match Enter 8 character salt:1234abcd Enter iteration count as a number (Eg: 44):120 Enter Keystore Alias:vault Initializing Vault Oct 17, 2014 12:58:11 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Vault Configuration in AS7 config file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="EAP_HOME/vault/vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"/> <vault-option name="KEYSTORE_ALIAS" value="vault"/> <vault-option name="SALT" value="1234abcd"/> <vault-option name="ITERATION_COUNT" value="120"/> <vault-option name="ENC_FILE_DIR" value="EAP_HOME/vault/"/> </vault><management> ... ******************************************** Vault is initialized and ready for use Handshake with Vault complete
Procédure 17.9. Exécutez la commande d'archivage sécurisé des mots de passe de façon non interactive
- Lancez l'interface en ligne de commande de votre système d'exploitation et exécutez la commande d'archivage sécurisé des mots de passe. Consultez la liste dans Aperçu général, et substituez les valeurs réservées par vos valeurs préférées.Utilisez
EAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server).vault.sh --keystore KEYSTORE_URL --keystore-password KEYSTORE_PASSWORD --alias KEYSTORE_ALIAS --vault-block VAULT_BLOCK --attribute ATTRIBUTE --sec-attr SEC-ATTR --enc-dir ENC_FILE_DIR --iteration ITERATION_COUNT --salt SALT
Exemple 17.18. Exécutez la commande d'archivage sécurisé des mots de passe de façon non interactive
vault.sh --keystore
EAP_HOME/vault/vault.keystore
--keystore-passwordvault22
--aliasvault
--vault-blockvb
--attributepassword
--sec-attr0penS3sam3
--enc-dirEAP_HOME/vault/
--iteration120
--salt1234abcd
Sortie de commande========================================================================= JBoss Vault JBOSS_HOME: EAP_HOME JAVA: java ========================================================================= Oct 17, 2014 2:23:43 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Secured attribute value has been stored in vault. Please make note of the following: ******************************************** Vault Block:vb Attribute Name:password Configuration should be done as follows: VAULT::vb::password::1 ******************************************** Vault Configuration in AS7 config file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="EAP_HOME/vault/vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"/> <vault-option name="KEYSTORE_ALIAS" value="vault"/> <vault-option name="SALT" value="1234abcd"/> <vault-option name="ITERATION_COUNT" value="120"/> <vault-option name="ENC_FILE_DIR" value="EAP_HOME/vault/"/> </vault><management> ... ********************************************
Votre mot de passe de keystore est masqué afin de pouvoir être utilisé dans les fichiers de configuration et de déploiement. De plus, votre archivage de sécurité est initialisé et prêt à l'utilisation.
17.7.6. Configurer JBoss EAP pour qu'il utilise l'archivage sécurisé des mots de passe
Avant de masquer les mots de passe et d'autres attributs sensibles dans les fichiers de configuration, vous devez sensibiliser JBoss EAP 6 à l'archivage sécurisé des mots de passe qui les stocke et les déchiffre.
Conditions préalables
Procédure 17.10. Activer le Password Vault
- Exécutez la commande suivante de gestion CLI, en substituant les valeurs d’espace réservé par celles de la sortie de la commande du Password Vault Section 17.7.5, « Initialiser le Password Vault ».
Note
Si vous utilisez Microsoft Windows Server, mettes deux caractères\\
dans le chemin d'accès au lieu d'un seul. Par exemple,C:\\data\\vault\\vault.keystore
. C'est parce qu'un simple caractère\
est utilisé comme caractère d'échappement./core-service=vault:add(vault-options=[("KEYSTORE_URL" => "PATH_TO_KEYSTORE"), ("KEYSTORE_PASSWORD" => "MASKED_PASSWORD"), ("KEYSTORE_ALIAS" => "ALIAS"), ("SALT" => "SALT"),("ITERATION_COUNT" => "ITERATION_COUNT"), ("ENC_FILE_DIR" => "ENC_FILE_DIR")])
Exemple 17.19. Activer le Password Vault
/core-service=vault:add(vault-options=[("KEYSTORE_URL" => "EAP_HOME/vault/vault.keystore"), ("KEYSTORE_PASSWORD" => "MASK-5dOaAVafCSd"), ("KEYSTORE_ALIAS" => "vault"), ("SALT" => "1234abcd"),("ITERATION_COUNT" => "120"), ("ENC_FILE_DIR" => "EAP_HOME/vault/")])
JBoss EAP 6 est configuré pour décrypter les strings masqués par l'intermédiaire de l'archivage sécurisé de mots de passe. Pour ajouter des strings à l'archivage sécurisé et les utiliser dans votre configuration, voir la section : Section 17.7.8, « Stocker une chaîne sensible dans l'archivage sécurisé des mots de passe ».
17.7.7. Configurer JBoss EAP pour qu'il utilise une implémentation d'archivage sécurisé personnalisée
Vous pouvez utiliser votre propre implémentation de SecurityVault
pour masquer les mots de passe, et les autres attributs sensibles dans les fichiers de configuration.
Conditions préalables
Procédure 17.11. Utilisez une implémentation sécurisée de l'archivage des mots de passe
- Créez une classe qui implémente l'interface
SecurityVault
. - Créez un module contenant la classe de l'étape précédente, et spécifiez une dépendance sur
org.picketbox
où l'interface estSecurityVault
. - Activez l'archivage de mots de passe sécurisée dans la configuration du serveur JBoss EAP en ajoutant l'élément d'archivage à l'aide des attributs suivants :
- code
- La nom complet de la classe qui implémente
SecurityVault
. - module
- Le nom du module qui contient la classe personnalisée.
Vous pouvez également utiliser les paramètresvault-options
pour initialiser la classe personnalisée d'un archivage de mots de passe.Exemple 17.20. Utilisez les paramètres
vault-options
pour initialiser la classe personnalisée/core-service=vault:add(code="custom.vault.implementation.CustomSecurityVault", module="custom.vault.module", vault-options=[("KEYSTORE_URL" => "PATH_TO_KEYSTORE"), ("KEYSTORE_PASSWORD" => "MASKED_PASSWORD"), ("KEYSTORE_ALIAS" => "ALIAS"), ("SALT" => "SALT"),("ITERATION_COUNT" => "ITERATION_COUNT"), ("ENC_FILE_DIR" => "ENC_FILE_DIR")])
JBoss EAP 6 est configuré pour décrypter les chaînes masquées par l'intermédiaire d'une implémentation personnalisée de l'archivage des mots de passe.
17.7.8. Stocker une chaîne sensible dans l'archivage sécurisé des mots de passe
Inclure les mots de passe et autres strings sensibles dans les fichiers de configuration en texte brut est un risque pour la sécurité. Stocker ces chaînes à la place dans le Password Vault pour améliorer la sécurité, où il peuvent être référencés dans les fichiers de configuration, commandes CLI Management et applications sous leur forme masquée.
Conditions préalables
Procédure 17.12. Stocker une chaîne sensible de manière interactive
Exécutez la commande d'archivage sécurisé des mots de passe
Lancez l'interface en ligne de commande de votre système d'exploitation et exécutez la commande d'archivage sécurisé des mots de passe. UtilisezEAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server). Démarrez une nouvelle session interactive en tapant0
(zéro).Complétez les paramètres de l'archivage sécurisé des mots de passe à l'invite.
Veuillez saisir les paramètres d'authentification nécessaires à l'invite. Ces valeurs doivent correspondre à celles indiquées lors de la création de l'archivage sécurisé des mots de passe.Note
Le mot de passe du keystore doit être fourni au format texte brut et non pas au format caché.Complétez les paramètres de l'archivage sécurisé des paramètres à l'invite.
Saisir0
(zéro) pour commencer à stocker le string sensible. Suivez les instructions pour saisir les paramètres qui conviennent.Notez les informations pour ce string masqué.
Un message s'affiche sur la sortie standard, montrant le bloc d'archivage sécurisé, le nom de l'attribut, le string masqué, et des conseils sur l'utilisation du string dans votre configuration. Prendre note de ces informations dans un emplacement sécurisé. Voici un exemple de sortie.Vault Block:ds_Example1 Attribute Name:password Configuration should be done as follows: VAULT::ds_Example1::password::1
Sortir de la console interactive
Saisir3
(trois) pour sortir de la console interactive.
Exemple 17.21. Stocker une chaîne sensible de manière interactive
========================================================================= JBoss Vault JBOSS_HOME: EAP_HOME/jboss-eap-6.4 JAVA: java ========================================================================= ********************************** **** JBoss Vault *************** ********************************** Please enter a Digit:: 0: Start Interactive Session 1: Remove Interactive Session 2: Exit 0 Starting an interactive session Enter directory to store encrypted files:11:18:46,086 INFO [org.jboss.security] (management-handler-thread - 4) PBOX0 Enter directory to store encrypted files:EAP_HOME/vault/ Enter Keystore URL:EAP_HOME/vault/vault.keystore Enter Keystore password: Enter Keystore password again: Values match Enter 8 character salt:1234abcd Enter iteration count as a number (Eg: 44):120 Enter Keystore Alias:vault Initializing Vault Oct 21, 2014 11:20:49 AM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Vault Configuration in AS7 config file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="EAP_HOME/vault/vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"/> <vault-option name="KEYSTORE_ALIAS" value="vault"/> <vault-option name="SALT" value="1234abcd"/> <vault-option name="ITERATION_COUNT" value="120"/> <vault-option name="ENC_FILE_DIR" value="EAP_HOME/vault/"/> </vault><management> ... ******************************************** Vault is initialized and ready for use Handshake with Vault complete Please enter a Digit:: 0: Store a secured attribute 1: Check whether a secured attribute exists 2: Remove secured attribute 3: Exit 0 Task: Store a secured attribute Please enter secured attribute value (such as password): Please enter secured attribute value (such as password) again: Values match Enter Vault Block:ds_Example1 Enter Attribute Name:password Secured attribute value has been stored in vault. Please make note of the following: ******************************************** Vault Block:ds_Example1 Attribute Name:password Configuration should be done as follows: VAULT::ds_Example1::password::1 ******************************************** Please enter a Digit:: 0: Store a secured attribute 1: Check whether a secured attribute exists 2: Remove secured attribute 3: Exit
Procédure 17.13. Stocker une chaîne sensible de manière non interactive
- Lancez l'interface en ligne de commande de votre système d'exploitation et exécutez la commande d'archivage sécurisé des mots de passe. Utilisez
EAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server).Remplacez les espaces réservés par vos propres valeurs. Les valeurs des paramètresKEYSTORE_URL
,KEYSTORE_PASSWORD
etKEYSTORE_ALIAS
doivent correspondre à celles fournies lors de la création de l'archivage sécurisé des mots de passe.Note
Le mot de passe du keystore doit être fourni au format texte brut et non pas au format caché.EAP_HOME/bin/vault.sh
--keystore KEYSTORE_URL --keystore-password KEYSTORE_PASSWORD --alias KEYSTORE_ALIAS --vault-block VAULT_BLOCK --attribute ATTRIBUTE --sec-attr SEC-ATTR --enc-dir ENC_FILE_DIR --iteration ITERATION_COUNT --salt SALT Notez les informations pour ce string masqué.
Un message s'affiche sur la sortie standard, montrant le bloc d'archivage sécurisé, le nom de l'attribut, le string masqué, et des conseils sur l'utilisation du string dans votre configuration. Prendre note de ces informations dans un emplacement sécurisé. Voici un exemple de sortie.Vault Block:vb Attribute Name:password Configuration should be done as follows: VAULT::vb::password::1
Exemple 17.22. Exécutez la commande d'archivage sécurisé des mots de passe de façon non interactive
EAP_HOME/bin/vault.sh
--keystoreEAP_HOME/vault/vault.keystore
--keystore-passwordvault22
--aliasvault
--vault-blockvb
--attributepassword
--sec-attr0penS3sam3
--enc-dirEAP_HOME/vault/
--iteration120
--salt1234abcd
========================================================================= JBoss Vault JBOSS_HOME: EAP_HOME JAVA: java ========================================================================= Oct 22, 2014 9:24:43 AM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Secured attribute value has been stored in vault. Please make note of the following: ******************************************** Vault Block:vb Attribute Name:password Configuration should be done as follows: VAULT::vb::password::1 ******************************************** Vault Configuration in AS7 config file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="EAP_HOME/vault/vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="vault22"/> <vault-option name="KEYSTORE_ALIAS" value="vault"/> <vault-option name="SALT" value="1234abcd"/> <vault-option name="ITERATION_COUNT" value="120"/> <vault-option name="ENC_FILE_DIR" value="EAP_HOME/vault/vault/"/> </vault><management> ... ********************************************
Ces chaînes sensibles sont maintenant stockées dans le Password Vault où il peuvent être référencés dans les fichiers de configuration, commandes CLI Management et applications sous leur forme masquée.
17.7.9. Utiliser un string sensible crypté dans Configuration.
Conditions préalables
Note
/host=HOST_NAME
à la commande si vous devez appliquer les changements à un domaine géré.
/core-service=SUBSYSTEM:read-resource-description(recursive=true)
Exemple 17.23. Énumérer la description de toutes les ressources dans le sous-système de gestion
/core-service=management:read-resource-description(recursive=true)
expressions-allowed
. Si la valeur est true
, vous pouvez alors utiliser des expressions dans la configuration de ce sous-système.
${VAULT::VAULT_BLOCK::ATTRIBUTE_NAME::MASKED_STRING}
Exemple 17.24. Définition de Datasource à l'aide d'un mot de passe masqué
ds_ExampleDS
et l'attribut est password
.
... <subsystem xmlns="urn:jboss:domain:datasources:1.0"> <datasources> <datasource jndi-name="java:jboss/datasources/ExampleDS" enabled="true" use-java-context="true" pool-name="H2DS"> <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url> <driver>h2</driver> <pool></pool> <security> <user-name>sa</user-name> <password>${VAULT::ds_ExampleDS::password::1}</password> </security> </datasource> <drivers> <driver name="h2" module="com.h2database.h2"> <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class> </driver> </drivers> </datasources> </subsystem> ...
17.7.10. Utiliser un string sensible crypté dans une Application
Conditions préalables
Exemple 17.25. Servlet qui utilise un mot de passe d'archivage sécurisé.
/*@DataSourceDefinition( name = "java:jboss/datasources/LoginDS", user = "sa", password = "sa", className = "org.h2.jdbcx.JdbcDataSource", url = "jdbc:h2:tcp://localhost/mem:test" )*/ @DataSourceDefinition( name = "java:jboss/datasources/LoginDS", user = "sa", password = "VAULT::DS::thePass::1", className = "org.h2.jdbcx.JdbcDataSource", url = "jdbc:h2:tcp://localhost/mem:test" )
17.7.11. Vérifiez si une chaîne sensible se situe dans l'archivage sécurisé des mots de passe
Avant de tenter de stocker ou utiliser une chaîne sensible dans l'archivage sécurisé des mots de passe, il peut être utile de confirmer si celle-ci est déjà stockée.
Procédure 17.14. Vérifier une chaîne sensible de manière interactive
Exécutez la commande d'archivage sécurisé des mots de passe
Lancez l'interface en ligne de commande de votre système d'exploitation et exécutez la commande d'archivage sécurisé des mots de passe. UtilisezEAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server). Démarrez une nouvelle session interactive en tapant0
(zéro).Complétez les paramètres de l'archivage sécurisé des mots de passe à l'invite.
Veuillez saisir les paramètres d'authentification nécessaires à l'invite. Ces valeurs doivent correspondre à celles indiquées lors de la création de l'archivage sécurisé des mots de passe.Note
Le mot de passe du keystore doit être fourni au format texte brut et non pas au format caché.- Saisissez
1
(un) pour sélectionner « Check whether a secured attribute exists » (Vérifiez l'existence d'un attribut sécurisé). - Veuillez saisir le nom du bloc d'archivage sécurisé dans lequel la chaîne sensible est stockée.
- Veuillez saisir le nom de la chaîne sensible à vérifier.
Si la chaîne sensible est stockée dans le bloc d'archivage sécurisé spécifié, vous obtiendrez un message de confirmation semblable au suivant :
Une valeur existe pour (VAULT_BLOCK, ATTRIBUTE)
Aucune valeur n'a été stockée pour (VAULT_BLOCK, ATTRIBUTE)
Exemple 17.26. Vérifier une chaîne sensible de manière interactive
========================================================================= JBoss Vault JBOSS_HOME: EAP_HOME JAVA: java ========================================================================= ********************************** **** JBoss Vault *************** ********************************** Please enter a Digit:: 0: Start Interactive Session 1: Remove Interactive Session 2: Exit 0 Starting an interactive session Enter directory to store encrypted files:EAP_HOME/vault Enter Keystore URL:EAP_HOME/vault/vault.keystore Enter Keystore password: Enter Keystore password again: Values match Enter 8 character salt:1234abcd Enter iteration count as a number (Eg: 44):120 Enter Keystore Alias:vault Initializing Vault Oct 22, 2014 12:53:56 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Vault Configuration in AS7 config file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="EAP_HOME/vault/vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"/> <vault-option name="KEYSTORE_ALIAS" value="vault"/> <vault-option name="SALT" value="1234abcd"/> <vault-option name="ITERATION_COUNT" value="120"/> <vault-option name="ENC_FILE_DIR" value="EAP_HOME/vault/"/> </vault><management> ... ******************************************** Vault is initialized and ready for use Handshake with Vault complete Please enter a Digit:: 0: Store a secured attribute 1: Check whether a secured attribute exists 2: Remove secured attribute 3: Exit 1 Task: Verify whether a secured attribute exists Enter Vault Block:vb Enter Attribute Name:password A value exists for (vb, password) Please enter a Digit:: 0: Store a secured attribute 1: Check whether a secured attribute exists 2: Remove secured attribute 3: Exit
Procédure 17.15. Vérifier une chaîne sensible de manière non-interactive
- Lancez l'interface en ligne de commande de votre système d'exploitation et exécutez la commande d'archivage sécurisé des mots de passe. Utilisez
EAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server).Remplacez les espaces réservés par vos propres valeurs. Les valeurs des paramètresKEYSTORE_URL
,KEYSTORE_PASSWORD-password
etKEYSTORE_ALIAS
doivent correspondre à celles fournies lors de la création de l'archivage sécurisé des mots de passe.Note
Le mot de passe du keystore doit être fourni au format texte brut et non pas au format caché.EAP_HOME/bin/vault.sh
--keystore KEYSTORE_URL --keystore-password KEYSTORE_PASSWORD --alias KEYSTORE_ALIAS --check-sec-attr --vault-block VAULT_BLOCK --attribute ATTRIBUTE --enc-dir ENC_FILE_DIR --iteration ITERATION_COUNT --salt SALT
Si la chaîne sensible est stockée dans le bloc d'archivage sécurisé spécifié, vous obtiendrez le message suivant :
Le mot de passe existe déjà
Ce mot de passe n'existe pas.
17.7.12. Supprimer une chaîne sensible de l'archivage sécurisé des mots de passe
Pour des raisons de sécurité, il est préférable de supprimer les chaînes sensibles de l'archivage sécurisé des mots de passe lorsqu'elles ne sont plus nécessaires. Par exemple, si vous désactivez une application, toute chaîne sensible utilisée dans une définition de base de données devra être supprimée en même temps.
Avant de supprimer une chaîne sensible de l'archivage sécurisé des mots de passe, confirmez si celle-ci est utilisée dans la configuration de JBoss EAP. Une méthode consiste à utiliser l'utilitaire « grep » pour rechercher les fichiers de configuration d'instances de la chaîne masquée. Sur Red Hat Enterprise Linux (et systèmes d'exploitation identiques), grep
est installé par défaut, mais il doit être installé manuellement pour Microsoft Windows Server.
Procédure 17.16. Supprimer une chaîne sensible de manière interactive
Exécutez la commande d'archivage sécurisé des mots de passe
Lancez l'interface en ligne de commande de votre système d'exploitation et exécutezEAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server). Démarrez une nouvelle session interactive en tapant0
(zéro).Saisir les informations d'authentification
Veuillez saisir les paramètres d'authentification nécessaires à l'invite. Ces valeurs doivent correspondre à celles indiquées lors de la création de l'archivage sécurisé des mots de passe.Note
Le mot de passe du keystore doit être fourni au format texte brut et non pas au format caché.- Veuillez saisir
2
(deux) pour choisir l'optionRemove secured attribute
(Supprimer l'attribut sécurisé). - Veuillez saisir le nom du bloc d'archivage sécurisé dans lequel la chaîne sensible est stockée.
- Veuillez saisir le nom de la chaîne sensible à supprimer.
Si la chaîne sensible est supprimée, vous obtiendrez un message de confirmation semblable au suivant :
Secured attribute [VAULT_BLOCK::ATTRIBUTE] has been successfully removed from vault
Secured attribute [VAULT_BLOCK::ATTRIBUTE] was not removed from vault, check whether it exist
Exemple 17.27. Supprimer une chaîne sensible de manière interactive
********************************** **** JBoss Vault *************** ********************************** Please enter a Digit:: 0: Start Interactive Session 1: Remove Interactive Session 2: Exit 0 Starting an interactive session Enter directory to store encrypted files:EAP_HOME/vault/ Enter Keystore URL:EAP_HOME/vault/vault.keystore Enter Keystore password: Enter Keystore password again: Values match Enter 8 character salt:1234abcd Enter iteration count as a number (Eg: 44):120 Enter Keystore Alias:vault Initializing Vault Dec 23, 2014 1:40:56 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Vault Configuration in configuration file: ******************************************** ... </extensions> <vault> <vault-option name="KEYSTORE_URL" value="EAP_HOME/vault/vault.keystore"/> <vault-option name="KEYSTORE_PASSWORD" value="MASK-5dOaAVafCSd"/> <vault-option name="KEYSTORE_ALIAS" value="vault"/> <vault-option name="SALT" value="1234abcd"/> <vault-option name="ITERATION_COUNT" value="120"/> <vault-option name="ENC_FILE_DIR" value="EAP_HOME/vault/"/> </vault><management> ... ******************************************** Vault is initialized and ready for use Handshake with Vault complete Please enter a Digit:: 0: Store a secured attribute 1: Check whether a secured attribute exists 2: Remove secured attribute 3: Exit 2 Task: Remove secured attribute Enter Vault Block:craft Enter Attribute Name:password Secured attribute [craft::password] has been successfully removed from vault
Procédure 17.17. Supprimer une chaîne sensible de manière non-interactive
- Lancez l'interface en ligne de commande de votre système d'exploitation et exécutez la commande d'archivage sécurisé des mots de passe. Utilisez
EAP_HOME/bin/vault.sh
(sur Red Hat Enterprise Linux et systèmes d'exploitation similaires) ouEAP_HOME\bin\vault.bat
(sur Microsoft Windows Server).Remplacez les espaces réservés par vos propres valeurs. Les valeurs des paramètresKEYSTORE_URL
,KEYSTORE_PASSWORD
etKEYSTORE_ALIAS
doivent correspondre à celles fournies lors de la création de l'archivage sécurisé des mots de passe.Note
Le mot de passe du keystore doit être fourni au format texte brut et non pas au format caché.EAP_HOME/bin/vault.sh
--keystore KEYSTORE_URL --keystore-password KEYSTORE_PASSWORD --alias KEYSTORE_ALIAS --remove-sec-attr --vault-block VAULT_BLOCK --attribute ATTRIBUTE --enc-dir ENC_FILE_DIR --iteration ITERATION_COUNT --salt SALT
Si la chaîne sensible est supprimée, vous obtiendrez un message de confirmation semblable au suivant :
Secured attribute [VAULT_BLOCK::ATTRIBUTE] has been successfully removed from vault
Secured attribute [VAULT_BLOCK::ATTRIBUTE] was not removed from vault, check whether it exist
Exemple 17.28. Supprimer une chaîne sensible de manière non-interactive
./vault.sh --keystore EAP_HOME/vault/vault.keystore --keystore-password vault22 --alias vault --remove-sec-attr --vault-block craft --attribute password --enc-dir ../vault/ --iteration 120 --salt 1234abcd ========================================================================= JBoss Vault JBOSS_HOME: EAP_HOME JAVA: java ========================================================================= Dec 23, 2014 1:54:24 PM org.picketbox.plugins.vault.PicketBoxSecurityVault init INFO: PBOX000361: Default Security Vault Implementation Initialized and Ready Secured attribute [craft::password] has been successfully removed from vault
17.8. Java Authorization Contract for Containers (JACC)
17.8.1. Java Authorization Contract for Containers (JACC)
17.8.2. Configurer la sécurité JACC (Java Authorization Contract for Containers)
jboss-web.xml
pour y inclure les paramètres qu'il faut.
Pour ajouter JACC au domaine de sécurité, ajouter la police d'autorisation JACC
à la pile d'autorisations du domaine de sécurité, avec l'indicateur requis
. Voici un exemple de domaine de sécurité avec support JACC. Cependant, le domaine de sécurité est configuré dans la console de gestion ou dans l'interface CLI, plutôt que directement dans le code XML.
<security-domain name="jacc" cache-type="default"> <authentication> <login-module code="UsersRoles" flag="required"> </login-module> </authentication> <authorization> <policy-module code="JACC" flag="required"/> </authorization> </security-domain>
Le fichier jboss-web.xml
se trouve dans WEB-INF/
de votre déploiement, et contient des ajouts ou remplacements de configuration spécifique JBoss pour le conteneur web. Pour utiliser votre domaine de sécurité activé-JACC, vous devrez inclure l'élément <security-domain>
, et aussi définir l'élément <use-jboss-authorization>
à true
. L'application suivante est configurée correctement pour pouvoir utiliser le domaine de sécurité JACC ci-dessus.
<jboss-web> <security-domain>jacc</security-domain> <use-jboss-authorization>true</use-jboss-authorization> </jboss-web>
La façon de configurer les EJB à utiliser un domaine de sécurité et JACC différe des applications Web. Pour un EJB, vous pouvez déclarer des method permissions sur une méthode ou sur un groupe de méthodes, dans le descripteur ejb-jar.xml
. Dans l'élément <ejb-jar>
, chaque élément <method-permission>
dépendant contient des informations sur les roles JACC. Voir l'exemple de configuration pour plus d'informations. La classe EJBMethodPermission
fait partie de Java Enterprise Edition 6 API, et est documentée dans http://docs.oracle.com/javaee/6/api/javax/security/jacc/EJBMethodPermission.html.
Exemple 17.29. Exemple de permissions de méthode JACC dans un EJB
<ejb-jar> <assembly-descriptor> <method-permission> <description>The employee and temp-employee roles may access any method of the EmployeeService bean </description> <role-name>employee</role-name> <role-name>temp-employee</role-name> <method> <ejb-name>EmployeeService</ejb-name> <method-name>*</method-name> </method> </method-permission> </assembly-descriptor> </ejb-jar>
jboss-ejb3.xml
qui se trouve dans l'élément enfant <security>
. En plus du domaine de sécurité, vous pouvez également spécifier le <run-as-principal>
, qui change le principal que l'EJB exécute.
Exemple 17.30. Exemple de déclaration de domaine de sécurité dans un EJB
<ejb-jar> <assembly-descriptor> <security> <ejb-name>*</ejb-name> <security-domain>myDomain</security-domain> <run-as-principal>myPrincipal</run-as-principal> </security> </assembly-descriptor> </ejb-jar>
17.9. Java Authentication SPI for Containers (JASPI)
17.9.1. Sécurité Java Authentication SPI pour Conteneurs (JASPI)
17.9.2. Configuration de la sécurité Java Authentication SPI pour conteneurs (JASPI)
<authentication-jaspi>
à votre domaine de sécurité. La configuration est similaire à celle d'un module d'authentification standard, mais les éléments de module de login sont inclus dans l'élément <login-module-stack>
. La structure de configuration est la suivante :
Exemple 17.31. Structure de l'élément authentication-jaspi
<authentication-jaspi> <login-module-stack name="..."> <login-module code="..." flag="..."> <module-option name="..." value="..."/> </login-module> </login-module-stack> <auth-module code="..." login-module-stack-ref="..."> <module-option name="..." value="..."/> </auth-module> </authentication-jaspi>
EAP_HOME/domain/configuration/domain.xml
ou dans le fichier EAP_HOME/standalone/configuration/standalone.xml
.
Chapitre 18. Single Sign On (SSO)
18.1. SSO (Single Sign On) pour les applications web
Single Sign On (SSO) autorise l'authentification à une ressource, en vue d'autoriser implicitement l'accès à d'autres ressources.
SSO non-clusterisée limite le partage des informations d'accès à des applications d'un même hôte virtuel. En outre, il n'y a aucun mécanisme de résilience en cas de défaillance de l'hôte. Les données d'authentification SSO clusterisées peuvent être partagées entre les applications de plusieurs hôtes et sont résistantes au basculement. De plus, SSO clusterisée est capable de recevoir des demandes d'un équilibreur de charges.
Si une ressource n'est pas protégée, un utilisateur n'a pas besoin de s'authentifier. Si un utilisateur accède à une ressource protégée, l'utilisateur devra s'authentifier.
18.2. SSO (Single Sign On) clusterisées pour les applications web
jboss-web.xml
.
18.3. Choisir l'implémentation SSO qui vous convient
web.xml
. Une SSO clusterisée permet la réplication des informations d'identité et de contexte de sécurité, indépendamment du fait de savoir si les applications sont elles-mêmes clusterisées. Bien que ces technologies peuvent être utilisées conjointement, elles repésentent des concepts distincts.
Si votre organisation utilise déjà une authentification basée Kerberos et un système d'autorisation comme Microsoft Active Directory, vos pourrez utiliser les mêmes systèmes pour vous authentifier de façon transparente à vos applications Enterprise en cours d'exécution sur JBoss EAP 6.
Si vous exécutez un certain nombre d'applications sur une seule instance et que vous avez besoin d'activer une réplication de session SSO pour ces applications, la SSO non clusterisée sera la solution indiquée.
Si vous exécutez une ou plusieurs applications dans un cluster et que vous avez besoin d'activer une réplication de session SSO pour toutes ces applications, la SSO clusterisée sera la solution indiquée.
18.4. Utilisation de SSO (Single Sign On) pour les applications web
Les fonctionnalités SSO (Single Sign On) sont fournies par les sous-systèmes web et Infinispan. Utilisez cette procédure pour configurer SSO dans les applications web.
Conditions préalables
- Vous devez avoir un domaine de sécurité configuré qui gère les authentifications et l'accès.
- Le sous-système
infinispan
. Présent, par défaut, dans tous les profils de domaines gérés ou de serveurs autonomes. - Le
web
cache-conteneur
et le cache-répliqué SSO. Les fichiers de configurations initiaux contiennent déjà le cache-conteneurweb
, et certaines des configurations contiennent déjà le cache-répliqué SSO également. Utilisez les commandes suivantes pour vérifier et activer le cache répliqué SSO. Notez que ces commandes modifient le profilha
d'un domaine géré. Vous pouvez modifier les commandes pour utiliser un profil différent, ou supprimer la portion/profile=ha
de la commande, pour un serveur autonome.Exemple 18.1. Vérifier le cache-conteneur
web
Les profils et les configurations mentionnées ci-dessus incluent le cache-conteneurweb
par défaut. Utilisez la commande suivante pour vérifier sa présence. Si vous utilisez un profil différent, remplacez par son nom à la place deha
./profile=ha/subsystem=infinispan/cache-container=web/:read-resource(recursive=false,proxies=false,include-runtime=false,include-defaults=true)
Si le résultat affiche unsuccess
, le sous-système sera présent. Sinon, vous devrez l'ajouter.Exemple 18.2. Ajouter le cache-conteneur
web
Utilisez les trois commandes suivantes pour activer le cache-conteneurweb
à votre configuration. Modifiez le nom du profil selon le cas, ainsi que les autres paramètres. Ici, les paramètres sont ceux utilisés dans une configuration par défaut./profile=ha/subsystem=infinispan/cache-container=web:add(aliases=["standard-session-cache"],default-cache="repl",module="org.jboss.as.clustering.web.infinispan")
/profile=ha/subsystem=infinispan/cache-container=web/transport=TRANSPORT:add(lock-timeout=60000)
/profile=ha/subsystem=infinispan/cache-container=web/replicated-cache=repl:add(mode="ASYNC",batching=true)
Exemple 18.3. Vérifier le cache-répliqué
SSO
Éxécutez la commande de Management CLI suivante :/profile=ha/subsystem=infinispan/cache-container=web/:read-resource(recursive=true,proxies=false,include-runtime=false,include-defaults=true)
Cherchez une sortie qui ressemble à ceci :"sso" => {
Si vous ne la trouvez pas, le cache-répliqué SSO ne sera pas présent dans votre configuration.Exemple 18.4. Ajouter le cache-répliqué
SSO
/profile=ha/subsystem=infinispan/cache-container=web/replicated-cache=sso:add(mode="SYNC", batching=true)
web
doit être configuré pour utiliser SSO. La commande suivante active SSO sur le serveur virtuel appelé default-host
et le domaine de cookies domaine.com
. Le nom de cache est sso
, et une nouvelle authentification est désactivée.
/profile=ha/subsystem=web/virtual-server=default-host/sso=configuration:add(cache-container="web",cache-name="sso",reauthenticate="false",domain="domain.com")
jboss-web.xml
et le même Realm dans son fichier de configuration web.xml
.
Configurer sso
sous le sous-système web dans le profil utilisateur. La version ClusteredSingleSignOn
est utilisée quand l'attribut cache-container
est présent, sinon la classe SingleSignOn
sera utilisée.
Exemple 18.5. Exemple de configuration SSO non clusterisée
/subsystem=web/virtual-server=default-host/sso=configuration:add(reauthenticate="false")
Une application peut rendre invalide une session en invoquant la méthode javax.servlet.http.HttpSession.invalidate()
.
18.5. Kerberos
18.6. SPNEGO
18.7. Microsoft Active Directory
- LDAP (Lightweight Directory Access Protocol) stocke les informations utilisateurs, ordinateurs, mots de passe et autres ressources.
- Kerberos procure une authentification sécurisée sur le réseau.
- DNS (Domain Service Name) fournit des mappages entre les adresses IP, les noms d'hôtes et les ordinateurs ou autres périphériques du réseau.
18.8. Configuration de Kerberos ou Microsoft Active Directory Desktop SSO pour les applications web
Pour authentifier vos applications web ou EJB à l'aide de l'authentification basée Kerberos existante de votre organisation et de votre infrastructure d'autorisation, comme Microsoft Active Directory, vous pouvez utiliser les fonctionnalités de JBoss Negotiation intégrées dans JBoss EAP 6. Si vous configurez votre application web correctement, une connexion réussie en desktop ou réseau sera suffisante pour vous authentifier en toute transparence dans votre application web, donc aucune invite de connexion supplémentaire ne sera nécessaire.
Il existe des différences notables entre la plateforme JBoss EAP 6 et les versions précédentes :
- Les domaines de sécurité sont configurés pour chaque profil d'un domaine géré, ou pour chaque serveur autonome. Ils ne font pas partie du déploiement lui-même. Le domaine de sécurité qu'un déploiement doit utiliser est nommé dans le fichier de déploiement
jboss-web.xml
oujboss-ejb3.xml
. - Les propriétés de sécurité sont configurées dans le cadre d'un domaine de sécurité. Elles ne font pas partie du déploiement.
- Vous pouvez ne plus remplacer les authentificateurs dans votre déploiement. Toutefois, vous pouvez ajouter une valve NegotiationAuthenticator à votre descripteur
jboss-web.xml
afin d'obtenir le même effet. La valve nécessite toujours les éléments<security-constraint>
et<login-config>
à définir dans le fichierweb.xml
. Ils servent à décider quelles ressources sont sécurisées. Cependant, l'auth-method choisie sera substituée par la valve NegotiationAuthenticator du fichierjboss-web.xml
. - Les attributs
CODE
des domaines de sécurité utilisent maintenant un nom simple au lieu d'un nom de classe complet. Le tableau suivant montre les mappages entre les classes utilisées pour JBoss Negotiation, et leurs classes.
Tableau 18.1. Codes de modules de connexion et Noms de classes
Nom simple | Nom de classe | But |
---|---|---|
Kerberos |
com.sun.security.auth.module.Krb5LoginModule
com.ibm.security.auth.module.Krb5LoginModule
|
Module de connexion Kerberos quand on utilise le JDK d'Oracle.
Module de connexion Kerberos quand on utilise le JDK d'IBM.
|
SPNEGO | org.jboss.security.negotiation.spnego.SPNEGOLoginModule | Le mécanisme qui permet aux applications web de s'authentifier à votre serveur d'authentification Kerberos. |
AdvancedLdap | org.jboss.security.negotiation.AdvancedLdapLoginModule | Utilisé avec les serveurs LDAP autres que Microsoft Active Directory. |
AdvancedAdLdap | org.jboss.security.negotiation.AdvancedADLoginModule | Utilisé avec les serveurs Microsoft Active Directory LDAP. |
JBoss Negotiation Toolkit
est un outil de débogage que vous pouvez télécharger à partir de https://community.jboss.org/servlet/JiveServlet/download/16876-2-34629/jboss-negotiation-toolkit.war. Il est fourni comme un outil supplémentaire pour vous aider à déboguer et tester les mécanismes d'authentification avant d'introduire votre application en production. C'est un outil non pris en charge, mais considéré comme très utile. Comme SPNEGO, cet outil peut être difficile à configurer pour les applications web.
Procédure 18.1. Installation de l'authentification SSO (Single Sign On) pour les Applications Web ou EJB
Configurer un domaine de sécurité qui représente l'identité de votre serveur. Définir les propriétés système si nécessaire.
Le premier domaine de sécurité authentifie le conteneur lui-même au service de répertoires. Il a besoin d'utiliser un module de connexion qui accepte un type de mécanisme de connexion statique, car un utilisateur réel n'est pas impliqué. Cet exemple utilise une entité de sécurité statique et fait référence à un fichier keytab qui contient les informations d'identification.Le code XML est donné ici dans un but de clarification, mais vous devez utiliser la console de gestion ou l'interface CLI pour configurer vos domaines de sécurité.<security-domain name="host" cache-type="default"> <authentication> <login-module code="Kerberos" flag="required"> <module-option name="storeKey" value="true"/> <module-option name="useKeyTab" value="true"/> <module-option name="principal" value="host/testserver@MY_REALM"/> <module-option name="keyTab" value="/home/username/service.keytab"/> <module-option name="doNotPrompt" value="true"/> <module-option name="debug" value="false"/> </login-module> </authentication> </security-domain>
Configurer un second domaine de sécurité pour sécuriser l'application web ou les applications. Définir les propriétés système si nécessaire.
Le deuxième domaine de sécurité est utilisé pour authentifier l'utilisateur particulier au serveur d'authentification Kerberos ou SPNEGO. Vous avez besoin d'au moins un module de connexion pour authentifier l'utilisateur et un autre à la recherche de rôles à appliquer à l'utilisateur. Le code XML suivant montre un exemple de domaine de sécurité SPNEGO. Il inclut un module d'autorisation pour mapper les rôles aux utilisateurs individuels. Vous pouvez également utiliser un module de recherche pour les rôles sur le serveur d'authentification lui-même.<security-domain name="SPNEGO" cache-type="default"> <authentication> <!-- Check the username and password --> <login-module code="SPNEGO" flag="requisite"> <module-option name="password-stacking" value="useFirstPass"/> <module-option name="serverSecurityDomain" value="host"/> </login-module> <!-- Search for roles --> <login-module code="UsersRoles" flag="required"> <module-option name="password-stacking" value="useFirstPass" /> <module-option name="usersProperties" value="spnego-users.properties" /> <module-option name="rolesProperties" value="spnego-roles.properties" /> </login-module> </authentication> </security-domain>
Spécifier la security-constraint et la login-config dans le fichier
web.xml
Le descripteurweb.xml
contient des informations sur les contraintes de sécurité et sur la configuration de la connexion. En voici des exemples :<security-constraint> <display-name>Security Constraint on Conversation</display-name> <web-resource-collection> <web-resource-name>examplesWebApp</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>RequiredRole</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>SPNEGO</auth-method> <realm-name>SPNEGO</realm-name> </login-config> <security-role> <description> role required to log in to the Application</description> <role-name>RequiredRole</role-name> </security-role>
Indiquer le domaine de sécurité et les autres paramètres dans le descripteur
jboss-web.xml
.Spécifiez le nom du domaine de la sécurité côté client (l'autre dans cet exemple) dans le descripteurjboss-Web.xml
de votre déploiement, pour obliger votre application à utiliser ce domaine de sécurité.Vous ne pouvez plus remplacer les authentificateurs directement. À la place, vous devez ajouter NegotiationAuthenticator comme valve au descripteurjboss-web.xml
si nécessaire.<jacc-star-role-allow>
vous permet d'utiliser un astérisque (*) pour faire correspondre plusieurs noms de rôles, et est optionnel.<jboss-web> <security-domain>SPNEGO</security-domain> <valve> <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name> </valve> <jacc-star-role-allow>true</jacc-star-role-allow> </jboss-web>
Ajouter une dépendance au
MANIFEST.MF
de votre application, pour trouver l'emplacement des classes Negotiation.L'application web a besoin d'une dépendance sur la classeorg.jboss.security.negotiation
à ajouter au manifesteMETA-INF/MANIFEST.MF
du déploiement pour pouvoir localiser les classes JBoss Negotiation. Ce qui suit vous montre une entrée formatée correctement.Manifest-Version: 1.0 Build-Jdk: 1.6.0_24 Dependencies: org.jboss.security.negotiation
- Sinon, ajouter une dépendance à votre application en modifiant le fichier
META-INF/jboss-deployment-structure.xml
:<?xml version="1.0" encoding="UTF-8"?> <jboss-deployment-structure> <deployment> <dependencies> <module name='org.jboss.security.negotiation'/> </dependencies> </deployment> </jboss-deployment-structure>
Votre application web accepte et authentifie les informations d'identification avec Kerberos, Active Directory de Microsoft ou autre service de répertoire compatible SPNEGO. Si l'utilisateur exécute l'application d'un système qui est déjà enregistré dans le service de répertoires, et où les rôles sont déjà appliqués à l'utilisateur, l'application web n'aura pas besoin d'authentification, et les fonctionnalités SSO seront opérationnelles.
18.9. Configurer SPNEGO avec un renvoi à l'authentification Form
Procédure 18.2. Sécurité SPNEGO avec renvoi à l'authentification Form
Configurer SPNEGO
Voir la procédure décrite dans Section 18.8, « Configuration de Kerberos ou Microsoft Active Directory Desktop SSO pour les applications web »Modifier le fichier
web.xml
Ajouter un élémentlogin-config
à votre application et configurer les pages de connexion et d'erreurs dans le fichier web.xml :<login-config> <auth-method>SPNEGO</auth-method> <realm-name>SPNEGO</realm-name> <form-login-config> <form-login-page>/login.jsp</form-login-page> <form-error-page>/error.jsp</form-error-page> </form-login-config> </login-config>
Ajouter un contenu web
Ajouter des references delogin.html
et deerror.html
àweb.xml
. Ces fichiers sont ajoutés à l'archive d'application web à l'emplacement spécifié dans la configurationform-login-config
. Pour obtenir des informations, voir la section Enable Form-based Authentication du Security Guide de JBoss EAP 6. Un fichierlogin.html
typique ressemble à ceci :<html> <head> <title>Vault Form Authentication</title> </head> <body> <h1>Vault Login Page</h1> <p> <form method="post" action="j_security_check"> <table> <tr> <td>Username</td><td>-</td> <td><input type="text" name="j_username"></td> </tr> <tr> <td>Password</td><td>-</td> <td><input type="password" name="j_password"></td> </tr> <tr> <td colspan="2"><input type="submit"></td> </tr> </table> </form> </p> <hr> </body> </html>
Note
18.10. Domaine du cookie
18.10.1. Cookie Domain
/
. Cela signifie que seul l'hôte d'origine peut lire le contenu d'un cookie. Le fait de configurer un domaine de cookie particulier rend le contenu du cookie disponible à un plus grand nombre d'hôtes. Pour configurer le domaine de cookie, veuillez vous référer à Section 18.10.2, « Configurer Cookie Domain ».
18.10.2. Configurer Cookie Domain
http://app1.xyz.com
et http://app2.xyz.com
à partager un contexte SSO, même si ces applications fonctionnent sur des serveurs différents dans un cluster ou si l'hôte virtuel avec lequel ils sont associés possède plusieurs alias.
Exemple 18.6. Exemple de configuration de domaine de cookie
<Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" cookieDomain="xyz.com" />
Chapitre 19. Références de sécurité pour le développement
19.1. Référence de paramètre de sécurité EJB
Tableau 19.1. Éléments de paramètres de sécurité EJB
Élément | Description |
---|---|
<security-identity>
|
Contient des éléments enfants relatifs à l'identité de sécurité d'un EJB.
|
<use-caller-identity />
|
Indique que l'EJB utilise la même identité de sécurité que l'appelant.
|
<run-as>
|
Contient un élément
<role-name> .
|
<run-as-principal>
|
Si présent, indique le principal assigné aux appels sortants. Si non présent, les appels sortants sont assignés à un principal nommé
anonymous .
|
<role-name>
|
Spécifie le role d'exécution de l'EJB.
|
<description>
|
Décrit le role nommé dans
. <role-name>
|
Exemple 19.1. Exemples d'identité de sécurité
<session>
.
<ejb-jar> <enterprise-beans> <session> <ejb-name>ASessionBean</ejb-name> <security-identity> <use-caller-identity/> </security-identity> </session> <session> <ejb-name>RunAsBean</ejb-name> <security-identity> <run-as> <description>A private internal role</description> <role-name>InternalRole</role-name> </run-as> </security-identity> </session> <session> <ejb-name>RunAsBean</ejb-name> <security-identity> <run-as-principal>internal</run-as-principal> </security-identity> </session> </enterprise-beans> </ejb-jar>
Chapitre 20. Références de configuration
20.1. Référence de configuration jboss-web.xml
Les descripteurs de déploiement jboss-web.xml
et web.xml
sont tous les deux mis dans le répertoire WEB-INF
du déploiement. jboss-web.xml
est un descripteur de déploiement d'une application web de JBoss EAP qui contient des options de configuration supplémentaires qui servent à des fonctionnalités supplémentaires de JBoss Web. Ce descripteur peut être utilisé pour remplacer les paramètres de configuration du descripteur web.xml
et définir les paraméètres spécifiques àJBoss EAP.
La plupart des paramètres de configuration font correspondre les prérequis définis dans le fichier web.xml
de l'application à des ressources locales. Pour plus d'explications sur les paramètres de configuration de web.xml
consulter http://docs.oracle.com/cd/E13222_01/wls/docs81/webapp/web_xml.html.
web.xml
requiert jdbc/MyDataSource
, alors jboss-web.xml
fera sans doute correspondre la source de données globale java:/DefaultDS
pour remplir ce besoin. Le WAR utilise la source de données globale pour faire correspondre jdbc/MyDataSource
.
Tableau 20.1. Attributs de haut niveau communs de jboss-web.xml
Attribut | Description |
---|---|
servlet |
L'élément de servelt spécifie les liaisons spécifiques au servlet.
|
max-active-sessions |
Indique le nombre maximum de sessions actives autorisées. Si le nombre de sessions gérées par le gestionnaire de sessions dépasse cette valeur et que la
passivation est activée, l'excès sera passivé sur la base du passivation-min-idle-time configuré.
Si défini sur -1, cela signifie aucune limite.
|
replication-config |
L'élément
replication-config est utilisé pour la réplication de sessions configurées dans le fichier jboss-web.xml .
|
passivation-config |
L'élément
passivation-config est utilisé pour configurer la passivation de sessions dans le fichier jboss-web.xml .
|
distinct-name |
L'élément
distinct-name indique le nom précis EJB3 de l'application web.
|
data-source |
Un mappage à
data-source requis par web.xml .
|
context-root | Le contexte root de l'application. La valeur par défaut est le nom du déploiement sans le suffixe .war . |
virtual-host | Le nom de l'hôte virtuel HTTP à partir duquel l'application accepte les requêtes. Se réfère au contenu de l'en-tête de l'Host HTTP. |
annotation | Décrit une annotation utilisée par l'application. Voir <annotation> pour plus d'information. |
listener | Décrit un listener utilisée par l'application. Voir <listener> pour plus d'information. |
session-config | Cet élément remplit la même fonction que l'élément <session-config> du fichier web.xml et est inclus dans un but de compatibilité seulement. |
valve | Décrit une valve utilisée par l'application. Voir <valve> pour plus d'information. |
overlay | Le nom d'une couche supplémentaire à ajouter à l'application. |
security-domain | Le nom du domaine de sécurité utilisé par l'application. Le domaine de sécurité lui-même est configuré à partir de la console de gestion basée web ou l'interface CLI. |
security-role | Cet élément remplit la même fonction que l'élément <session-role> du fichier web.xml et est inclus dans un but de compatibilité seulement. |
jacc-star-role-allow |
L'élément
jacc-star-role-allow indique si l'agent générant les permissions jacc de la couche web a besoin de générer une permission WebResourcePermission pour qu'un fournisseur jacc puisse prendre la décision de passer outre l'autorisation ou non.
|
use-jboss-authorization | Si cet élément est présent et contient la valeur non sensible à la casse "true", la pile d'autorisation web JBoss sera utilisée. S'il n'est pas présent ou contient une valeur non "true", alors seuls les mécanismes d'autorisation indiqués dans les spécifications Java Enterprise Edition seront utilisés. Cet élément est nouveau dans JBoss EAP 6. |
disable-audit | Définir cet élément de booléen à false pour activer et à true pour désactiver l'auditing web. L'auditing de sécurité web ne fait pas partie de la spécification Java EE. Cet élément est nouveau dans JBoss EAP 6. |
disable-cross-context | Si sur false , l'application sera en mesure d'appeler un autre contexte d'application. La valeur par défaut est true . |
enable-websockets | Définir cet élément à true dans jboss-web.xml pour spécifier si l'accès websocket doit être actif pour l'application web. |
<annotation>
.
Tableau 20.2. Éléments de configuration d'une annotation
Attribut | Description |
---|---|
class-name |
Nom de la classe de l'annotation
|
servlet-security |
L'élément, comme
@ServletSecurity , qui représente la sécurité du servlet.
|
run-as |
L'élément, comme
@RunAs , qui représente l'information run-as
|
multipart-config |
L'élément, comme
@MultiPart , qui représente l'information multi-config.
|
<listener>
.
Tableau 20.3. Éléments de configuration d'un listener
Attribut | Description |
---|---|
class-name |
Nom de la classe du listener
|
listener-type |
Liste les éléments de
condition qui indiquent quelle sorte de listener ajouter au contexte de l'application. Les choix valides sont les suivants :
|
module |
Le nom du module qui contient la classe du listener.
|
param |
Un paramètre. Contient deux éléments enfants,
<param-name> et <param-value> .
|
Chapitre 21. Références supplémentaires
21.1. Types d'archives Java
Tableau 21.1.
Type d'archive | Extension | But | Exigences pour la structure de répertoire |
---|---|---|---|
Archive Java | .jar | Contient les bibliothèques de classes Java. |
Le fichier
META-INF/MANIFEST.MF (optionnel), spécifiant des informations comme quelle classe est la classe principale .
|
Archive Web | .war |
Contient des fichiers Java Server Pages (JSP), des servlets, et des fichiers XML, en plus des classes et des bibliothèques. Le contenu Archive Web s'appelle aussi Web Application.
|
Le fichier
WEB-INF/web.xml , qui contient des informations sur la structure de l'application web. Il y a également d'autres fichiers présents dans WEB-INF/ .
|
Resource Adapter Archive | .rar |
La structure du répertoire est dans la spécification JCA.
|
Contient un adaptateur de ressources Java Connector Architecture (JCA). S'appelle également un connecteur.
|
Enterprise Archive | .ear |
Utilisé par Java Enterprise Edition (EE) pour empaqueter un ou plusieurs modules dans une simple archive, pour que les modules puissent être déployés dans le serveur d'applications simultanément. Maven et Ant sont les outils les plus communément utilisés pour générer les archives EAR.
|
Le répertoire
META-INF/ qui contient un ou plusieurs fichiers de descripteurs de déploiement XML.
|
N'importe quel type de module.
| |||
Service Archive | .sar |
Ressemble à une archive Enterprise, spécifique à Enterprise Application Platform.
|
Répertoire
META-INF/ contenant un fichier jboss-service.xml ou jboss-beans.xml .
|
Annexe A. Historique des révisions
Historique des versions | |||
---|---|---|---|
Version 6.4.0-16 | Tuesday April 14 2015 | Lucas Costi | |
|