2. Un premier exemple

2.1. Introduction

Une application JEE nécessite un environnement assez complexe à comprendre et à construire. La première chose dont ce type d'application a besoin pour fonctionner est un serveur d'application. Là où une application web n'avait besoin que d'un serveur Tomcat (ou autre), une application entreprise doit fonctionner dans un serveur d'applications, plus important, et plus complexe. D'ailleurs, la plupart des serveurs d'application intègrent un serveur Tomcat en tant que module, pour gérer leurs applications web. Un serveur d'application est un élément logiciel, qui formellement ressemble à un serveur Tomcat. La différence est qu'il expose plus de fonctionnalités, et est capable de fournir de nombreux services. Citons-en ici quelques-uns :
  • Il possède un module d'annuaire, appelé JNDI (Java Naming Directory Interface), qui enregistre toutes les ressources que ce serveur gère et expose. Lorsque l'on a besoin d'un ressource particulière, on peut la demander à l'annuaire, à partir de son nom.
  • Il permet de gérer des EJB (Enterprise Java Beans), que nous allons voir en détails dans la suite de ce chapitre.
  • Il permet de gérer des applications web, et de répondre aux requêtes HTTP sur le port 80 (entre autres).
  • Il est capable de se connecter à des bases de données, et d'exposer ces connexions sous forme de data sources . Il gère les transactions, distribuées ou non, de ces bases.
  • Il sait gérer des files d'attentes de messages, qu'il peut ensuite redistribuer à des abonnés (Java Messaging System).
  • Il peut se connecter à un service de messagerie ( mail ), et d'exposer ce service dans son annuaire.
  • Il gère la journalisation de tous ces éléments, de même que le suivi des performances, et différentes fonctions de monitoring .
  • Enfin, il a une vision globale de tous les composants qu'il gère, et il est capable de les faire dialoguer les uns avec les autres, notamment sous forme d'injection de dépendances.

2.2. Installation dans Glassfish à l'aide de Netbeans

De même que pour la partie JPA, nous allons utiliser Netbeans 6.9.1 pour cette présentation. La première étape consiste à créer un projet Java EE > Enterprise application. Nous verrons les détails du packaging de ce projet dans la suite. Pour le moment, disons simplement que notre modèle JPA et nos EJB seront des modules de ce projet.
Création d'une application d'entreprise

Figure 26. Création d'une application d'entreprise


Le deuxième étape consiste à donner un nom à ce projet, que nous appellerons "cours-ear".
Nommage du projet JEE

Figure 27. Nommage du projet JEE


La dernière étape est importante : c'est elle qui donne le serveur d'application cible, et la version de JEE que l'on va utiliser. Ici, on sélectionne Glassfish v3, et JEE5, au lieu de JEE6 qui est sélectionné par défaut. On décochera également la création de l'application web, que nous écrirons dans un deuxième temps.
Choix du serveur d'application et de la version de JEE

Figure 28. Choix du serveur d'application et de la version de JEE


La structure du projet créé par Netbeans est la suivante.
Structure du projet final

Figure 29. Structure du projet final


2.3. Création d'un premier EJB

Sans entrer dans les détails de ce qu'est un EJB, disons simplement que techniquement, un EJB est une interface et une classe qui implémente cette interface. De plus, un EJB est géré par un serveur d'application (ici Glassfish), qui l'expose au travers de son annuaire JNDI. Les implémentations de nos EJB sont entièrement gérées par Glassfish, nous allons donc les ranger dans le projet cours-ear-ejb. En revanche, nos interfaces vont être utilisées par d'autres modules de notre application, non gérés par Glassfish. Nous allons donc les ranger dans un autre module. On crée pour cela un nouveau projet, de type Java > Java application, et on le nomme cours cours-ear-ejb-interfaces.
Création d'un module d'interfaces

Figure 30. Création d'un module d'interfaces


Ce module est pour le moment indépendant de notre application. Créons une première interface dans ce projet, appelée MarinService. Notre projet ressemble alors à la figure suivante.
Structure du projet interfaces

Figure 31. Structure du projet interfaces


Le code de notre interface est pour le moment minimaliste !

Exemple 56. Une première interface d'EJB

package org.paumard.ejb.service ;

 public  interface MarinService {

     public  long createMarin(String nom) ;
}

Qu'est-ce qui différencie l'interface d'un EJB d'une interface normale ? La réponse tient en un seul mot : rien. Créons à présent l'implémentation de cet EJB. Netbeans propose un processus automatique de création d'EJB, mais il ne nous convient pas, car il ne permet pas de créer un EJB qui implémente l'interface que nous venons d'écrire. Dans un premier temps, il faut mettre notre module cours-ear-ejb-interfaces en dépendance du module cours-ear-ejb. Pour cela, il faut ouvrir les propriétés du projet cours-ear-ejb, et cliquer sur le bouton Add project... dans l'onglet Librairies. Il n'y a plus qu'à sélectionner le projet cours-ear-ejb-interfaces et à valider.
Ajout du projet interface en dépendance du projet EJB

Figure 32. Ajout du projet interface en dépendance du projet EJB


Il n'y a plus qu'à créer une classe dans ce projet, appelons-la MarinServiceImpl.
Structure du projet EJB avec implémentation

Figure 33. Structure du projet EJB avec implémentation


Cette classe MarinServiceImpl doit implémenter l'interface MarinService. De plus, pour en faire un EJB, il faut l'annoter. L'EJB que nous allons écrire est un EJB stateless , il doit donc porter l'annotation @Stateless. Il existe aussi des EJB stateful , que l'on annote avec @Stateful. La deuxième annotation que nous ajoutons est @Remote, et nous lui donnons l'interface implémentée en paramètre. Nous aurions pu mettre cette annotation sur notre interface MarinService, mais cela nous aurait obligé à créer une dépendance de notre module d'interfaces vers la librairie JEE qui définit les annotations. Cette façon de faire est donc plus légère. Le code final de notre classe d'implémentation est le suivant.

Exemple 57. Une première implémentation d'un EJB

package org.paumard.ejb.service.impl ;

 @Stateless(mappedName="MarinService")
 @Remote(MarinService.class)
 public  class MarinServiceImpl  implements MarinService {

     @Override
     public  long createMarin(String nom) {
       
         return -1L ;
    }

}

L'argument de l'annotation @Stateless est destiné à l'annuaire du serveur qui va gérer cet EJB. Il indique le nom de cet EJB dans l'annuaire. On peut l'omettre, dans ce cas un nom par défaut est choisi. On constate que Netbeans a bien pris cette classe comme un EJB : il apparaît maintenant sous son nom dans le nœud Enterprise beans de notre projet cours-ear-ejb. Pour le moment notre EJB ne fait pas grand chose : il n'expose qu'une seule méthode, qui retourne systématiquement -1. Mais il est suffisant pour terminer de créer l'environnement technique dans lequel il va vivre.

2.4. Déploiement de notre premier EJB

Déployer un EJB consiste à construire un fichier archive EAR (Enterprise Application Archive), analogue aux fichiers WAR des applications web, dans un serveur d'application. De même que pour le fichier WAR, le fichier EAR a une structure particulière et spécifiée dans le standard JEE. Ce fichier EAR est géré par le module maître de notre application cours-ear. Pour que cet EAR fonctionne, il faut lui dire qu'il doit prendre en dépendance le projet cours-ear-ejb-interfaces. Il faut pour cela faire deux choses :
  • ajouter ce projet en dépendance du projet cours-ear dans l'onglet Librairies des propriétés de ce projet ;
  • ajouter ce projet dans l'onglet packaging, en précisant qu'il doit aller dans le répertoire lib.
Ajout du projet interface en dépendance de l'EAR

Figure 34. Ajout du projet interface en dépendance de l'EAR


Packaging du projet EAR

Figure 35. Packaging du projet EAR


On remarque sur cette deuxième figure que le projet d'interfaces se trouve bien dans le répertoire lib, que l'on peut changer dans l'interface de NetBeans en double-cliquant dessus. Une fois ces deux opérations menées à bien, il suffit de lancer le projet EAR. Deux onglets s'ouvrent alors dans Netbeans : le premier correspond au fichier de journalisation de Glassfish, et le second nous indique que notre EAR a été correctement déployé.

2.5. Création d'un client

Une EJB est comme une servlet : une fois lancé, rien ne se passe, et il faut créer une application cliente pour la voir en action. Dans le cas d'une servlet ou d'une page JSP, l'application cliente est un navigateur web. Dans le cas d'un EJB, le client est une méthode main() qu'il faut écrire à la main. Cette méthode doit faire trois choses :
  • Elle doit se connecter à l'annuaire du serveur d'application. L'ouverture de cette connexion requiert des dépendances spécifiques à chaque serveur d'application.
  • Une fois connectée, elle doit interroger l'annuaire avec le nom de la ressource dont elle a besoin. Si cette ressource existe bien, l'annuaire retourne un objet qui implémente l'interface associée à cette ressource.
  • Une fois en possession de cet objet, il suffit d'invoquer ses méthodes pour y accéder. Ici cet objet implémentera notre interface MarinService, nous pourrons donc invoquer sa méthode createMarin(String), et vérifier qu'elle nous retourne bien -1.
Pour porter ce code client, on crée un projet supplémentaire dans Netbeans, appelé cours-ejb-client. Ce projet a besoin de deux dépendances :
  • une dépendance vers le projet d'interfaces, puisqu'il va utiliser l'interface MarinService ;
  • une dépendance vers des éléments de Glassfish : glassfish-naming.jar, gf-client.jar et glassfish-corba-orb.jar.
Ces trois derniers JARs se trouvent dans $GLASSFISH_HOME/modules. Pour pouvoir se connecter à l'annuaire d'un serveur d'application, une application cliente a besoin d'un certain nombre d'informations, qui sont fixées de façon standard dans des variables d'environnement. La façon la plus simple de spécifier ces variables est de les écrire dans un fichier de propriétés standard : jndi.properties, qui doit se trouver à la racine de notre application cliente. Voyons le contenu de ce fichier dans le cas de Glassfish.

Exemple 58. Fichier jndi.properties dans le cas de Glassfish

java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl

Le code suivant va alors être capable de se connecter à notre EJB.

Exemple 59. Une première connexion à un EJB

public  class CheckEJB {

     public  static  void main(String... args)  throws NamingException {
        
         // création du "contexte initial" = de la connexion à l'annuaire du serveur
        InitialContext context =  new InitialContext() ;
        
         // requête sur le nom de la ressource que l'on veut, ici notre EJB
        MarinService marinService = (MarinService)context.lookup("MarinService") ;
        
         // invocation d'une méthode
         long id = marinService.createMarin("Surcouf") ;

        System.out.println("Id = " + id) ;
    }
}

La structure de notre projet cours-ejb-client est la suivante.
Structure du projet ejb-client

Figure 36. Structure du projet ejb-client


JPA & EJB
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction
1. Objet du mapping objet / relationnel
2. Un peu d'histoire
Un premier exemple
1. Introduction
2. Création de l'environnement technique
2.1. Introduction
2.2. Création de la base Derby
2.3. Création du projet NetBeans et d'une première entité
2.4. Structure d'un projet persistant
2.5. Une première classe persistante
2.6. Un premier fichier persistence.xml
3. Utilisation de ce premier exemple
3.1. Écriture du code d'utilisation
3.2. Exécution de notre premier exemple
3.3. Modification de la class Marin
3.4. Opérations CRUD
Mettre un jeu de classes en base
1. Introduction
2. Définition d'une entité JPA
2.1. Écriture de l'entité
2.2. Annotation de l'entité
2.3. Annotations des champs
2.4. Exemple d'utilisation
3. Opérations sur les entités
3.1. Introduction
3.2. Opération PERSIST
3.3. Opération REMOVE
3.4. Opération REFRESH
3.5. Opération DETACH
3.6. Opération MERGE
4. Mise en relation d'entités
4.1. Introduction
4.2. Relations unidirectionnelles et bidirectionnelles
4.3. Relation 1:1
4.4. Relation 1:p
4.5. Relation p:1
4.6. Relation n:p
4.7. Comportement cascade
4.8. Effacement des entités orphelines
5. Charger des entités et leurs relations
6. Objets inclus
6.1. Introduction
6.2. Déclaration d'un objet inclus
6.3. Utilisation d'objets inclus
6.4. Cas où l'objet inclus est nul
6.5. Renommer les colonnes incluses
6.6. Collections d'objets inclus
L'API Collection en base
1. Introduction
2. Enregistrer une collection d'entités
2.1. Enregistrement d'une collection simple
2.2. Enregistrement d'un Set
2.3. Enregistrement d'une List
3. Enregistrer une collection de types de base
4. Enregistrement d'une Map
4.1. Table de hachage de type (type de base, entité)
4.2. Cas où la clé est un champ de la valeur
4.3. Cas d'une table (entité, entité)
Héritage
1. Introduction
2. Enregistrement d'une hiérarchie de classes
2.1. Entité et super-classe non enregistrée
2.2. Position du problème
2.3. Trois façons de faire
3. Stratégie SINGLE_TABLE
3.1. Fonctionnement
3.2. Mise en place
3.3. Limitations
4. Stratégie JOINED
4.1. Fonctionnement
4.2. Mise en place
4.3. Limitations
5. Stratégie TABLE_PER_CLASS
5.1. Fonctionnement
5.2. Mise en place
5.3. Limitations
Requêtes
1. Introduction
2. Un premier exemple
2.1. Écriture d'une première requête
2.2. Exécution d'une première requête
2.3. Exécution d'une première requête d'agrégation
3. Définition de requêtes
3.1. Requêtes dynamiques
3.2. Requêtes paramétrées
3.3. Requêtes nommées
3.4. Requêtes natives
4. Exécution, analyse du résultat
4.1. Exécution d'une requête dynamique
4.2. Exécution d'une requête nommée
4.3. Analyse du résultat
4.4. Cas des résultats de grande taille
4.5. Remarques
5. Clause From
5.1. Définition des entités
5.2. Jointures dans la clause From
5.3. Remarque finale sur les jointures en JPQL
6. Clause Where
6.1. Variables et chemins dans une clause where
6.2. Expressions conditionnelles et opérateurs
6.3. Requêtes imbriquées
6.4. Opérateurs any, all et some
6.5. Expressions fonctionnelles
7. Clauses Group By et Having
8. Opérations Update et Delete
EJB
1. Introduction
2. Un premier exemple
2.1. Introduction
2.2. Installation dans Glassfish à l'aide de Netbeans
2.3. Création d'un premier EJB
2.4. Déploiement de notre premier EJB
2.5. Création d'un client
3. Mise en oeuvre du pattern session facade
3.1. Introduction
3.2. Modèle objet
3.3. Définition de l'unité de persistance
3.4. Assemblage de notre application
3.5. Assemblage et déploiement
3.6. Utilisation du client
4. Opération de persistance en façade
4.1. Introduction
4.2. Enrichissement du service
4.3. Création de la méthode findMarinById(long)
4.4. Ajout de la méthode findAllMarins()
4.5. Utilisation dans un code client
5. Types d'EJB
5.1. Introduction
5.2. Qu'est-ce qu'un EJB ?
5.3. Écriture d'un EJB session
5.4. Qu'est-ce qu'une méthode métier ?
5.5. EJB avec ou sans état
5.6. Gestion des transactions
5.7. Restrictions
6. Cycle de vie d'un EJB
6.1. Cas des EJB sans état
6.2. Cas des EJB avec état
6.3. Injection de dépendances
7. Transaction gérée par l'EJB
7.1. Introduction
7.2. Déclaration du mode transactionnel
7.3. Gestion de la transaction
7.4. Fonctionnement de la transaction
7.5. Cas des EJB avec état
8. Transaction gérée par le serveur
8.1. Introduction
8.2. Déclaration du mode transactionnel
8.3. Gestion de la transaction
8.4. Fonctionnement de la transaction
8.5. Remarques
9. Intercepteurs
9.1. Introduction
9.2. Aperçu général
9.3. Cycle de vie d'un intercepteur
9.4. Object InvocationContext
9.5. Interception d'un EJB ou d'une méthode métier
9.6. Exemple de mise en œuvre d'un intercepteur