Benoît CHARROUX EJB tutoriel Résumé Les Enterprise Java Bean (EJB) constituent le socle des applications Java pour les entreprises. Ce sont en effet des composants logiciels qui implémentent la partie métier d'une application. Associés à un support de persistance (typiqement un SGBD) qui conserve l'état de ces composants, et s'exécutant dans un serveur d'applications, ils permettent aux développeurs de ne programmer que la partie métier d'une application, laissant à un outil de génération automatique de code le soin de générer des services comme la persistance, les transactions, la sécurité, l'accès à distance, .... Longtemps délaissés car complexes à programmer et aux performances médiocre, ils sont à présent, avec la version 3, beaucoup plus utilisés car simples et fiables. Ce tutoriel présente les concepts principaux des EJB à travers plusieurs exemples. Pré-requis Avant de lire ce toturiel, il est fortement conseillé d'étudier Hibernate car le service de persistance dans la version 3 des EJB reprend largement les concepts d'Hibernate3. Pour exécuter les programme de ce tutoriel, vous devez avoir installé Eclipse et le serveur d'applications JBoss (reportez-vous à l'annexe pour voir comme procéder aux installations nécessaires). ______________________________________________________________________________________________ 1 / 38
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Benoît CHARROUX
EJB tutoriel
Résumé
Les Enterprise Java Bean (EJB) constituent le socle des applications Java pour les entreprises. Ce sont en effet des composants logiciels qui implémentent la partie métier d'une application. Associés à un support de persistance (typiqement un SGBD) qui conserve l'état de ces composants, et s'exécutant dans un serveur d'applications, ils permettent aux développeurs de ne programmer que la partie métier d'une application, laissant à un outil de génération automatique de code le soin de générer des services comme la persistance, les transactions, la sécurité, l'accès à distance, .... Longtemps délaissés car complexes à programmer et aux performances médiocre, ils sont à présent, avec la version 3, beaucoup plus utilisés car simples et fiables.
Ce tutoriel présente les concepts principaux des EJB à travers plusieurs exemples.
Pré-requis
Avant de lire ce toturiel, il est fortement conseillé d'étudier Hibernate car le service de persistance dans la version 3 des EJB reprend largement les concepts d'Hibernate3.
Pour exécuter les programme de ce tutoriel, vous devez avoir installé Eclipse et le serveur d'applications JBoss (reportez-vous à l'annexe pour voir comme procéder aux installations nécessaires).
Le premier composant que nous allons développer est constitué d'une classe composée d'une méthode qui retourne la chaîne «coucou».
Voici son code à la norme EJB :
package com.efrei.content_providing;import javax.ejb.*;@Statelesspublic class CoucouBean implements Coucou{
public String disCoucou() {return "coucou";
}
}
Vous remarquerez que, à l'exception de l'annotation @Stateless, c'est du code Java à norme POJO (Plain Old Java Object). @Stateless signifie que le composant n'a pas d'attribut, ou plus précisément que son état, matérialisé par des attributs, n'est pas conservé entre deux appels de méthodes.
L'interface Coucou (qui est implémentée par CoucouBean) s'écrit comme suit :
L'annotation @Remote signifie que ce composant peut être accédé par un client distant (distant par rapport au composant qui s'exécute dans le serveur d'application).
Une fois le projet créé, supprimer les répertoires META-INF. Le fichier « META-INF/ejb-jar.xml » n'est pas indispensable car il contient des informations qui peuvent être indiquées en utilissant les annotations de EJB3.
Vérifier ensuite, en éditant les propriétés du projet, qu'il dispose des librairies indiquées à la figure suivante. Si ce n'est pas le cas, ajoutez les librairies manquantes en cliquant sur Add External JARs.
Ajoutez au projet le code de la classe CoucouBean et de l'interface Coucou (voir le code en début de chapitre).
Vérifiez ensuite en éditant les propriétés du projet (par un clic droit sur le nom du projet), que c'est bien le serveur JBoss qui va exécuter votre projet :
Votre application est maintenant prête à être exécutée par JBoss. Ce serveur d'applications peut contenir plusieurs applications et il faut lui indiquer que c'est la votre seule qu'il doit exécuter. Pour cela, il faut ouvrir la Vue Serveur accessible via le menu Window -> Show view -> Server. Cliquez droit sur JBoss dans la Vue Serveur et ouvrez la boite de dialogue Add and Remove Projects :
Ajoutez-y votre projet. Il est tant à présent de démarrer le serveur.
Suivez le déroulement du lancement dans la console. Du code est alors généré automatiquement pour augmenter les fonctionnalités de votre composant. Les principaux services générés automatiquement sont : l'accès à distance par un programme client, la gestion de la concurrence d'accès, éventuellement la sécurité, ... C'est la tout l'intérêt des EJB. Tout ce code généré automatiquement déchargent le développeur de son écriture, indispensable, mais très longue à écrire. Une fois le serveur démarré, la confirmation du déploiement de votre application sera indiquée dans la console par des commentaires à peu près semblable à ceux-çi :
12:35:32,953 INFO [EJBContainer] STARTED EJB: com.efrei.tp1.BonjourBean ejbName: BonjourBean12:35:33,031 INFO [EJB3Deployer] Deployed: file:/E:/Program Files/jboss-4.0.5.GA/server/default/deploy/TP_1.jar
Le programme client pourrait résider sur une autre machine, mais pour faciliter le développement et le test de l'application complète, nous allons créer un autre projet dans Eclipse (sans fermer évidemment le projet côté serveur qui est en train de s'exécuter).
Créer donc un nouveau projet Java. En allant de fenêtre en fenêtre dans la configuration de votre nouveau projet, vous tomberez sur la fenêtre Java Settings. C'est là que vous allez indiquer que le projet client utilise le projet côté serveur préalablement créé :
Vous-y voyez la façon standard d'accéder à un composant via la méthode lookup de la classe Context. C'est l'accès au service de nommage Java Naming and Directory Interface (JNDI) qui permet, en connaissant le nom d'un objet distant, de récupérer une interface distante (reportez-vous au code de l'interface Coucou et rappelez-vous qu'elle est annotée @Remote). Il est important de remarquer que l'objet qui réalise cette interface réside sur le serveur distant (c'est une instance de la classe CoucouBean), et que l'interface à travers laquelle passe le programme du client ci-dessus cache ce qu'on appel un stub (ou une amorçe en Français), c'est à dire, un code généré automatiquement et qui transmet la demande d'exécution des méthode de l'interface au serveur via un protocole réseau à la norme Corba par exemple. Imaginez la complexité du code d'un tel stub pour vous rendre compte de l'intérêt des EJB.
Pour configurer correctement JNDI, vous devez placer un fichier appelé jndi.properties à la raçine des fichiers source du projet client. Remplissez ce fichier avec les instructions suivantes :
Un Entity Bean, ou plus simplement un Entity, est un objet léger et persistant. Persistant car son état, défini par la valeur de ses atttributs, est sauvegardé dans une base de données, léger car c'est un objet métier de la couche Model (dans une architecture Model View Controller). Typiquement un Entity est mappé avec une rangée dans une table d'une base de données. Pour un développeur, le support de persistance (le SGBD) est accessible via une l'API Java Persistence. Ceux qui connaissent Hibernate seront en terrain familier car cette API s'en inspire fortement (d'ailleurs, Hibernate est une des implémentations possible de cette API).
Pour respecter le model MVC, un Entity (faisant partie de la couche Model), est sous le contrôle d'un ou plusieurs objets Controller (au sans MVC du terme). La couche Controller contenant la logique applicative d'une application. Cette couche est réalisée par des Session Beans. Le schéma est donc le suivant :
L'application que nous allons développer est un mini gestionnaire pour une médiathèque qui se limite à la gestion sommaire des oeuvres que cette médiathèque contient. Voici pour commencer le code de l'entité OeuvreBean :
Vous remarquerez que, mis à part les annotations @Entity, ..., c'est du code Java classique. Le champs id va servir de clef primaire lors du mapping objet/relationnel. Une clef primaire est obligatoire mais, mis à part l'utilisation d'un champ unique comme clef primaire, il y a d'autres solutions comme d'écrire un classe, ou encore de combiner plusieurs champs. Le but étant toujours le même : établir l'unicité. C'est l'annotation @id qui indique que ce champ contiendra la clef primaire. Les autres annotations (@TableGenerator et @GeneratedValue) indiquent que cette clef primaire doit être générée automatiquement (elle pourrait aussi être valuée par programmation). Pour connaître toutes les règles d'écriture d'un Entity, reportez-vous au cours.
on y voit que c'est Hibernate qui est utilisé. La source de données pour gérer les transactions (jta-data-source) est la source incluse par défaut avec JBoss (en l'occurence, il s'agit de HSQLDB).
public void addOeuvre( String titre, String auteur );public List<Oeuvre> getOeuvres();
}
Cette interface, conformément à une interface niveau Controller, définit les traitements appliqués à l'Entity OeuvreBean. En l'occurence, ajouter une nouvelle oeuvre à la médiathèque et en obtenir la liste. Attention cependant, le méthode getOeuvres retourne une liste d'instance d'une classe Oeuvre, et non pas, comme on pourrait s'y attendre, des instances de OeuvreBean. Ceci afin de respecter strictement la modèle MVC et la règle suivante : les méthodes d'un controller ne doivent jaamais prendre en argument, n'y retourner, un objet de la couche Model. En effet, la couche Controller est utilisée par la couche View, et si les méthodes d'un controller prennent en argument ou retrourne un objet du Model, cet objet est exposé à la couche View, la couche Controller est alors inutile car court-circuité. Voici le code de la classe Oeuvre :
package com.efrei.mediatheque;import java.io.Serializable;public class Oeuvre implements Serializable{
Il n'y a pas d'annotation dans cette classe. Ce n'est donc ni un Session Bean, ni un Entity. Par contre, cette classe implémente l'interface Serializable car elle pourra être envoyée, du Session Bean qui s'exécute sur le serveur, jusqu'au niveau View qui réside peut-être dans un client distant.
Pour finir la description du code de l'application, voici le code du Session Bean (l'implémentation de l'interface GestionnaireOeuvre) :
}catch( Exception ex ){ throw new EJBException(ex.getMessage()); }
}}
La différence majeure par rapport au Session Bean que nous avons présenté au début de ce tutoriel, c'est la présence de l'EntityManager em. Il sert ici à gérer la persistance des Entities dans la méthode addOeuvre, ainsi qu'à rechercher un Entity dans la méthode getOeuvres. Les lecteurs habitués à Hibernate remarquerons l'équivalence en un EntityManager et une session Hibernate. La méthode getOeuvres lance une requête au SGBD afin de récupérer un liste d'OeuvreBean, parcours cette liste, créée des instances d'Oeuvre à l'image des OeuvreBean, et retroune une liste d'oeuvres.
Avant de démarre le serveur, vous devez ajouter toutes les classes et interface ci-dessus au projet, puis indiquer à JBoss le nouveau projet qu'il doit exécuté :
Il est temps à présent de démarrer JBoss. Le projet est alors déployé ce qui entraîne la création d'une table dans la base de données pour y stocker les Entities.
Créer maintenant un Java Project pour le client (procédez comme pour le bean de session et n'oubliez pas d'ajouter le fichier jndi.properties). Voici le code du client à ajouter à ce projet :
package test;
import java.util.Iterator;import java.util.List;import javax.naming.Context;import javax.naming.InitialContext;import com.efrei.mediatheque.GestionnaireOeuvre;import com.efrei.mediatheque.Oeuvre;public class Main{
public static void main( String[] argv ){try{
Context context = new InitialContext();GestionnaireOeuvre gestionnaireOeuvre =
Les codes sources de cette application sont à l'adresse :
http://perso.efrei.fr/~charroux/ejb/
RSS est un format de description de contenu. Acronyme de Really Simple Syndication, c’est un format qui contient des balises XML. Chaque balise donne des renseignements sur un contenu. La balise title par exemple donne le titre du contenu, tandis que la balise description en donne un résumé. D’autres balises permettent de classer le contenu (category). D’autres encore indiquent la date de publication (pubDate).
http://cyber.law.harvard.edu/rss/rss.html
Tout type de contenu peut être décrit : un site Web, des images, du texte, … Un contenu est référencé sur Internet par certaines balises (link par exemple).
Le diagramme des classes suivant représente une vision objet d'un fil RSS :
L'application à développer consiste à gérer à distance une base de données de fils RSS. Commençons par définir dans l'interface d'un Session Bean les méthodes de notre application principale :
Ces méthodes, utilisables par un client distant, permettent d'ajouter et de supprimer un fil RSS dans la base de données.
Un fil RSS (le canal avec ses items, etc.) est issu d'un fichier XML qui a été parsé. Les items, et toutes les données de ce fil sont liés au canal qui donne quant à lui une description globale du fil (un titre général, un résumé des items, ...). Puisque un fil RSS forme un tout, il faut laisser au client distant le soin de renseigner tous les champs utiles avant d'envoyer le fil complet au serveur. C'est pourquoi, la méthode addChannel ci-dessus reçoit en argument un canal complet (avec ses items associés, ...).
EJB tutorielDe plus, pour se conformer aux recommandations de programmation des EJB, il faut que
les arguments des méthodes d'un Session Bean accessibles à distance aient une granularité assez importante pour limiter les appels distants coûteux en temps de transmission. Cette remarque conforte notre idée de transmettre un canal complet.
Donnons à présent le code résumé de la classe Channel qui comme l'indique le diagramme des classes ci-dessus contient, entre autre, une composition de 1 vers plusieurs avec la classe Item (les autres associations se programment selon le même modèle) :
public class Channel implements Serializable{private URL link;...
// association 1 vers plusieurs avec la classe Itemprivate Vector<Item> items;
public void addItem( Item item ){if( items.contains(item) == false ){
items.add( item );}
}
public List<Item> getItems(){return items;
}}
Passons à présent à la programmation de la persistance en base de données d'un canal à l'aide d'Entities. Là, il convient de gérer plus finement la mémoire associée aux tables de la base de données, et ce, pour au moins deux raisons :
– il n'est pas raisonable d'avoir autant de table qu'il y a de classes dans le modèle objet ci-dessus d'autant que certaines classes ne contiennent que quelques données (la classe Source par exemple n'en contient que deux) ;
– certaines balises étant communes à un canal et ses items (link et description par exemple), nous pouvons les regrouper dans une classe de base appelée Rss, puis construire un canal et ses items à l'aide de deux classes hérités, soient respectivment RSSChannel et RSSItem.
En tenant compte de ces remarques, on obtient le diagramme des classes suivant :
Pour réduire encore le nombre de tables, la stratégie de mapping de l'héritage retennue est une table par hiérarchie de classes : il n'y aura donc qu'une table pour les classes Rss, RssChannel et RssItem.
De plus, les éléments cloud, image et enclosure ont été implantés comme des classes embarquées (voir le code ci-dessous). Elles seront donc mappées en base par des enregistrements binaires (VARBINARY).
@Entity@Inheritance(strategy=SINGLE_TABLE)public class RssChannel extends Rss{
public class RssCloud implements Serializable{private String domain;...
Ce découpage en classes de base et classes héritées aurait pu se faire aussi côté client mais :
– il faut maintenir le modèle de programmation côté client le plus simple possible ;– les quelques données qui sont dupliquées quand on n'a pas recourt à l'héritage devraient
pouvoir être transmises avec toutes les autres dans la même trame réseau et ainsi ne pas augmenter le trafic réseau ;
– il peut être inclus dans Eclipse auquel cas seule l'installation d'Eclipse est nécessaire ;– il peut être installé après Eclipse.
Dans tous les cas, utiliser la version 3.3.x d'Eclipse.
Attention cependant à la version de JBoss. La version 4.0.5 de base (qui se présente sous forme d'un fichier ZIP) par exemple n'intègre par un conteneur pour les EJB version 3. Dans ce cas, il faut récupérer une version qui, avec un installateur graphique, vous permettra de configurer JBoss. Un telle version est disponible à l'adresse :