(publié le 16/03/2007 -
Versions utilisées : Eclipse 3.2.1 - WTP 1.5.2 - JBoss 4.0.5 -
JDK 5.0)
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.
|
|
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éé, supprimer 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 :
Conclusion
Ce tutorial a montré comment faire ses premiers
pas avec les EJB 3 en utilisant Eclipse WTP et JBoss. Dans un futur article,
nous étudierons le sous-projet Dali dont l'objectif est de fournir
des outils pour le développement d'applications utilisant JPA.
|