Tutorial : développer des EJB 3 avec Eclipse et JBoss
La spécification EJB 3 dispose d'atouts indéniables
pour redorer le blason des EJB. D'une part elle
simplifie le processus de développement en allégeant
le code, d'autre part les lacunes des EJB Entity sont
largement comblées par une nouvelle spécification
qui traite spécialement du problème de la persistance
: JPA (Java Persistence API).
Ce tutorial à pour but de détailler les étapes de mise
en place d'un environnement technique (Eclipse et
JBoss) permettant de découvrir le développement
d'EJB 3.
Sommaire
• 1 Mise en place
o 1.1 Installation de JBoss
avec le support des EJB 3
o 1.2 Déclaration du serveur
JBoss dans WTP
• 2 Développement d'EJB 3
o 2.1 Création et configuration
d'un projet
o 2.2 Développement d'un EJB
Session
o 2.3 Tester l'EJB Session
o 2.4 Développement d'un EJB
Entity
o 2.5 Tester l'EJB Entity
Mise en place
Pour ce tutorial, la mise en place consiste en l'installation d'un JDK, d'Eclipse Web Tools
et de JBoss.
La procédure d'installation de WTP est décrite dans notre tutorial 'Développement de
Servlets et JSP avec Eclipse WTP'.
Installation de JBoss avec le support des EJB 3
Pour pouvoir développer et tester des EJB 3 il faut naturellement disposer d'un serveur
intégrant le support de cette nouvelle version de la spécification EJB. JBoss 4.0.5 répond
à ce besoin.
Le téléchargement de base (fichier zip) de JBoss 4.0.5 n'intègre pas le conteneur EJB 3.
Pour installer JBoss avec le support des EJB 3, il faut utiliser l'installeur graphique :
télécharger ce fichier contenant JBoss 4.0.5 et son installeur graphique.
Une fois téléchargé, exécuter l'installeur en double-cliquant sur le fichier (si les fichiers
JAR ne sont pas associés à Java, l'exécution de l'installeur peut se faire en utilisant la
ligne de commande suivante : java -jar ems-installer-1.2.0.CR1.jar ).
Suivre les étapes de l'installeur, après avoir indiqué l'emplacement il faut choisir un type
d'installation : sélectionner 'ejb3' :
Dans les pages suivantes de l'installeur, conserver les valeurs par défaut, excepté pour la
page 'JMX Security'. Dans cette page décocher toutes les cases et saisir un mot de
passe pour la console d'administration ('admin' pour faire simple) :
(PS: décocher ces cases permet d'éviter des problèmes lors de l'arrêt du serveur JBoss à
partir de la vue 'Serveurs' de WTP).
Déclaration du serveur JBoss dans WTP
Pour associer le serveur JBoss à WTP nous allons procéder de la même façon que pour le
serveur Tomcat (cf notre tutorial 'Développement de Servlets et JSP avec Eclipse WTP') :
- Ouvrir la page de préférences Préférences->Serveur->Environnements
d'exécution installés.
- Dans la liste proposée par le bouton 'Ajouter...', sélectionner le type de serveur 'JBoss
v4.0'.
- Dans la page suivante de l'assistant, indiquer l'emplacement du serveur JBoss.
- Ouvrir la perspective J2EE et sélectionner la vue 'Serveurs'.
- Utiliser le menu contextuel : 'Création->Serveur'.
- Vérifier que le type de serveur sélectionné est 'JBoss v4.0' et cliquer sur 'Terminer'
pour demander la création du serveur.
Une fois ces étapes de configuration effectuées, tester le bon fonctionnement du serveur
en demandant son exécution à partir de la vue 'Serveurs'.
Développement d'EJB 3
Création et configuration d'un projet
WTP 1.5 cible J2EE 1.4, les assistants ne prennent donc pas en compte les particularités
des EJB 3. C'est le cas notamment de l'assistant de création de projet EJB qui cible les
EJB2.
L'utilisation d'un projet de type EJB présente tout de même un intérêt : la vue 'Serveurs'
gère la publication des projets de type EJB vers le serveur de test.
Utiliser le menu contextuel de la vue 'Explorateur de projets' pour créer un projet EJB:
Dans la première page de l'assistant indiquer le nom du projet et sélectionner 'JBoss
v4.0' comme 'environnement d'exécution cible'. L'association du projet à un EAR n'est
pas nécessaire, il est donc inutile de cocher la case correspondante. Les autres pages de
l'assistant ne nécessitent pas de modification, cliquer sur le bouton 'Terminer'.
Une fois le projet créé, supprimé le répertoire META-INF pour éviter que WTP affiche
des erreurs liées à la validation du fichier ejb-jar.xml. Le fichier 'META-INF/ejb-
jar.xml' est optionnel pour le développement des EJB 3, les informations qui
apparaissaient dans ce fichier peuvent maintenant être placées directement dans le code
en utilisant le mécanisme d'annotations du JDK 5.0.
Le chemin de compilation du projet doit être complété avec l'ajout des fichiers jar
permettant l'utilisation de l'API EJB3. Dans la section 'Chemin de génération Java' de
la page de propriétés du projet, sélectionner l'onglet 'Bibliothèques', et utiliser le
bouton 'Ajouter des fichiers JAR externes...' pour ajouter les 3 fichiers JAR
nécessaires :
(NB: pour éviter de reproduire cette opération à la création de chaque projet, il est
possible d'utiliser la notion de 'bibliothèque utilisateur' en passant par le bouton 'Ajouter
une bibliothèque').
Développement d'un EJB Session
La création d'un EJB Session est largement simplifiée par la spécification EJB 3 : il n'y a
pas de contrainte d'héritage et aucune méthode particulière à implémenter.
Le développement d'un EJB 3 Session se fait en trois étapes.
Définition de l'interface du composant. Créer une interface contenant le code suivant :
package com.et;
public interface PremierEJB3 {
public String ditBonjour(String aQui);
}
Implémentation du composant. Créer cette classe :
package com.et;
public class PremierEJB3Bean implements PremierEJB3 {
public String ditBonjour(String aQui) {
return "Bonjour " + aQui + " !!!";
}
}
Ajout des annotations EJB 3 :
Ajouter les annotations nécessaires (@Remote et @Stateless). L'éditeur d'Eclipse 3.2
gère complètement la notion d'annotations du JDK 5.0, la complétion (Ctrl+espace)
fonctionne pour les annotations et permet d'ajouter simplement la directive 'import'
nécessaire :
Code de l'interface après l'ajout de l'annotation @Remote :
package com.et;
import javax.ejb.Remote;
@Remote
public interface PremierEJB3 {
public String ditBonjour(String aQui);
}
Code de la classe après l'ajout de l'annotation @Stateless :
package com.et;
import javax.ejb.Stateless;
@Stateless
public class PremierEJB3Bean implements PremierEJB3 {
public String ditBonjour(String aQui) {
return "Bonjour " + aQui + " !!!";
}
}
Tester l'EJB Session
Le code de notre EJB étant écrit, nous pouvons le tester. Pour ce faire, il faut d'une part
déployer le projet EJB dans le serveur JBoss et d'autre part écrire une application cliente.
Déployer le projet EJB.
A partir de la vue 'Serveurs', sélectionner le serveur JBoss et utiliser l'option 'Ajouter et
supprimer des projets...' du menu contextuel pour déployer le projet EJB :
Démarrer le serveur JBoss (en mode déboguage de préférence) pour voir si le projet est
bien pris en compte.
Si l'EJB est correctement déployé les lignes suivantes doivent apparaître dans la
console :
22:14:06,312 INFO [Ejb3Deployment] EJB3 deployment time took: 312
22:14:06,453 INFO [JmxKernelAbstraction] installing MBean:
jboss.j2ee:jar=IntroEJB3.jar,name=PremierEJB3Bean,service=EJB3 with dependencies:
22:14:06,906 INFO [EJBContainer] STARTED EJB: com.et.PremierEJB3Bean ejbName:
PremierEJB3Bean
22:14:07,078 INFO [EJB3Deployer] Deployed: file:/C:/Produits/jboss-
4.0.5.GA/server/default/deploy/IntroEJB3.jar
Créer une application de test .
Créer un projet Java et configurer son 'Chemin de génération Java' :
- Dans l'onglet 'Projets' ajouter le projet contenant les EJB :
- Dans l'onglet 'Bibliothèques' ajouter les JAR nécessaires à l'application cliente :
A la racine du projet créer un fichier nommé jndi.properties contenant les informations
qui permetteront à l'application cliente de se connecter au service de nommage du
serveur JBoss :
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
Créer la classe de test suivante pour invoquer l'EJB précédemment créé :
package com.et;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class ClientPremierEJB3 {
public static void main(String[] args) {
try {
Context context = new InitialContext();
PremierEJB3 beanRemote = (PremierEJB3)
context.lookup("PremierEJB3Bean/remote");
System.out.println(beanRemote.ditBonjour("ClientPremierEJB3"));
} catch (NamingException e) {
e.printStackTrace();
}
}
}
(Pour l'exécution : sélectionner la classe dans la vue 'Explorateur de projets' et utiliser
le menu 'Exécuter->Exécuter en tant que->Application Java').
Développement d'un EJB Entity
La spécification EJB 3 revoit très largement le développement des Entity Beans. Les EJB
Entity sont décrits dans une spécification complémentaire nommée JPA (Java Persistence
API) dont les principaux apports sont :
- Simplification du code via l'utilisation de l'approche 'POJO' (Plain Old Java Object) : un
EJB Entity consiste en une classe Java standard (pas de contrainte d'héritage, pas de
méthode particulière à implémenter).
- Fonctionnalités de mapping objet-relationnel plus riches : par exemple dans les
versions précédentes le format de définition de la correspondance entre les objets et les
structures de données relationnelles (le mapping objet-relationnel) n'était pas
standardisée. La spécification JPA propose d'utiliser les annotations pour définir le
mapping. Des problématiques comme l'héritage ou l'optimisation via l'utilisation de
requêtes SQL sont prises en compte. De façon générale, JPA aborde de façon complète et
pragmatique le problème de persistance alors que les spécifications EJB précédentes
adoptaient une approche plutôt dogmatique et faisaient l'impasse sur des fonctionnalités
essentielles permettant de mieux gérer le compromis entre transparence et optimisation.
- Utilisation possible en dehors d'un serveur d'applications J2EE : JPA peut être utilisée
dans une application cliente, la présence d'un conteneur d'EJB n'est plus nécessaire. Des
solutions intermédiaires sont aussi possibles : déploiement dans Tomcat d'une
application à base de Servlets et JSP utilisant JPA.
Le développement d'un EJB Entity est relativement simple. Ajouter la classe suivante
au projet précédemment créé :
package com.et;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Produit implements Serializable {
@Id
private String id;
private String libelle;
private int quantiteEnStock;
public Produit() {
super();
}
public Produit(String id) {
this.id = id;
}
public Produit(String id, String libelle, int quantiteEnStock) {
this.id = id;
this.libelle = libelle;
this.quantiteEnStock = quantiteEnStock;
}
public String getLibelle() {
return libelle;
}
public void setLibelle(String libelle) {
this.libelle = libelle;
}
public int getQuantiteEnStock() {
return quantiteEnStock;
}
public void setQuantiteEnStock(int quantiteEnStock) {
this.quantiteEnStock = quantiteEnStock;
}
public String getId() {
return id;
}
public String toString() {
return "Produit n°" + id + " - " + libelle + " - quantité disponible : " +
quantiteEnStock;
}
Seules les annotations @Entity et @Id distinguent cette classe d'une classe non
persistente.
La spécification JPA propose des API pour gèrer le cycle de vie d'un objet persistant.
Pour illustrer de façon simple ces APIs, nous allons créer un EJB Session. Dans le projet
EJB créer l'interface et la classe suivante :
package com.et;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface GestionDeStock {
public void ajouter(Produit produit);
public Produit
rechercherProduit(String id);
public List<Produit>
listerTousLesProduits();
}
package com.et;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class GestionDeStockBean implements GestionDeStock {
@PersistenceContext
EntityManager em;
public void ajouter(Produit produit) {
em.persist(produit);
}
public Produit rechercherProduit(String id) {
return em.find(Produit.class, id);
}
public List<Produit> listerTousLesProduits() {
return em.createQuery("SELECT p FROM Produit p ORDER BY
p.quantiteEnStock").getResultList();
}
Tester l'EJB Entity
La spécification JPA standardise l'utilisation d'un fichier, nommé persistence.xml, qui
permet de préciser des paramètres techniques liés au mapping objet-relationnel, par
exemple le nom de la 'DataSource' à utiliser pour se connecter à la base de données.
Pour tester notre EJB entity, nous devons définir le fichier persistence.xml et préciser la
DataSource que nous voulons utiliser. Pour simplifier la mise en oeuvre nous proposons
d'utiliser la bases de données HSQLDB intégrée au serveur JBoss. Cette base de données
est bien adaptée pour cette prise en main des EJB 3 : elle est démarrée
automatiquement avec le serveur JBoss et une DataSource est déjà définie vers cette
base. D'autre part, l'implémentation JPA de JBoss, qui se base sur Hibernate, est en
mesure de créer automatiquement la structure relationnelle correspondant à notre EJB.
Dans le projet EJB, créer le sous-répertoire META-INF et y placer le fichier
persistence.xml suivant :
<persistence>
<persistence-unit name="IntroEJB3">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto"
value="update"/>
</properties>
</persistence-unit>
</persistence>
(Le nom 'IntroEJB3' est purement arbitraire, le nom de la DataSource est celui défini par
JBoss, la valeur 'update' de la propriété 'hibernate.hbm2ddl.auto' indique que nous
souhaitons qu'Hibernate crée la structure de données automatiquement et la mette à
jour si nécessaire).
Redémarrer le serveur JBoss (la création des tables ne se fait que lors du lancement).
JBoss intègre un outil permettant de manipuler la base de données HSQLDB. Cet outil
peut nous permettre de lister les tables, de consulter les données qui s'y trouvent ainsi
que d'exécuter diverses requêtes SQL. Pour ouvrir cet outil : accéder à la console
d'administration de JBoss via l'url http://localhost:8080/jmx-console, dans la
section nommée 'jboss', cliquer sur 'database=localDB,service=Hypersonic'. Dans la
page qui s'affiche, cliquer sur le bouton 'Invoke' qui se trouve sous la signature de
méthode 'void startDatabaseManager()'. L'outil 'HSQL Database Manager' est alors
lancé (NB: cet outil est une application cliente, il ne s'affiche pas dans le navigateur).
Vérifier la présence de la table 'PRODUIT' :
Créer l'application cliente suivante pour tester le fonctionnement des EJB :
package com.et;
import java.util.Iterator;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class GestionDeStockClient {
public static void main(String[] args) {
try {
Context context = new InitialContext();
GestionDeStock stock = (GestionDeStock)
context.lookup("GestionDeStockBean/remote");
// Ne pas faire l'ajout plusieurs fois, commenter ces lignes après la
première exécution.
stock.ajouter(new Produit("001", "Tomate", 100));
stock.ajouter(new Produit("002", "Pomme de terre", 5680));
stock.ajouter(new Produit("003", "Orange", 23));
stock.ajouter(new Produit("004", "Carotte", 115));
stock.ajouter(new Produit("005", "Muscadet", 48));
List<Produit> produits = stock.listerTousLesProduits();
for (Iterator iter = produits.iterator(); iter.hasNext();) {
Produit eachProduit = (Produit) iter.next();
System.out.println(eachProduit);
}
} catch (NamingException e) {
e.printStackTrace();
}
}
}
Vérifier avec l'outil 'HSQL Database Manager' que les données ont bien été insérées :