Vendredi 26 novembre 2005, Valtech proposait un séminaire sur les Java Enterprise Bean version 3 (EJB3). La présentation a été effectuée par Denis Peyrusaube de Valtech Training. Après avoir fait un état des lieux, nous avons vu les alternatives existantes aujourd’hui pour combler certaines lacunes des EJB 2.1. Dans la deuxième partie de la présentation nous avons jugés des différences avec différents exemples d’EJB3 utilisant le conteneur J2EE Jboss 4.0.3.
1) Etat des lieux
Les spécifications EJB 2.1 publiées en novembre 2003 définissent 3 types d’EJB:
- Les EJB Session
- Les EJB Entité
- Les EJB messages (MDB)
Les EJB Session représentent souvent une façade au processus métier d’une application. Lourd à programmer (3 classes nécessaires) et à tester, les EJB sessions sont souvent utilisés en mode local par un client de type Servlet ou JSP. Par experience, il s’est averé que l’interface distribuée (remote interface) est peu utilisée alors que l’aspect distribution devait être l’un des points forts des EJB 2.1. Bien souvent les développeurs utilisent des patterns (Facade, DAO, Service Locator) pour contourner les limitationes de l’architecture des EJB 2.1 ou pour simplifier leur application.
Les EJB Entités sont le type d’EJB le plus décrié. Au départ destiné à représenter des données, ils sont lourds à développer et à tester. Ils nécessitent un mapping objet/relationnel soit via le container (CMP Container Managed Persistance) soit via l’application (BMP Bean Managed Persistance). Or le mode BMP, rarement utilisé, laisse trop de travail de programmation sans garanties de performance. Le mode d’exécution distribué (local/remote) est à proscrire compte tenu des gros problèmes de performance engendrés. Aujourd’hui les entity sont remplacés par le pattern Value-Object en utilisant un DAO. Il y a très peu de projet J2EE viable utilisant les Entity distribués sur le réseau car la sérialisation/déserialisation est extrement coûteuse. A noter aussi qu’entre une application J2EE dans un EAR et une application Web packagée dans un WAR, la sérialisation est utilisée, ce qui est une catastrophe en terme de perfs. Seul JBoss et le system de ProxyPattern est capable d’éviter cette sérialisation trop couteuse aujourd’hui.
Les EJB messages (MDB) sont des ejb asynchrones qui s’exécutent par exemple lorsqu’un message est publié sur une queue ou une topic JMS. Ils permettent d’effectuer des traitements de type batchm envoi d’email pour mot de passe perdus, validation vers un back-office… Rarement utilisé et complexe à tester, ils seront aussi améliorés par la spécification des EJB3.0.
Parmi les freins du succès: modèle de programmation contraignant qui ne permet pas d’héritage ni de polymorphisme. Le modèle de persistance proposé par les entity n’est pas très performant. L’indépendance vis-à-vis des serveurs J2EE est relative, il faut configurer souvent des fichiers XML par serveur d’application.
2) Les alternatives aujourd’hui
Face à ces lacunes, différentes initiatives souvent open-source proposent depuis 2 ans des compléments fonctionnels très puissant qui ont assuré le succès des EJB. Nous avons vu une présentation de JDO, Hibernate, Inversion Of Control, les annotations et la programmation par aspect (AOP). Je vais rapidement donner quelques détails sur chacune de ces technos mais sinon le web est plein d’articles très intéressant à ce sujet.
JDO (Java Data Object) est une spécification proposée par SUN. C’est un mécanisme de persistance basé sur des POJO (class Java représentant un object très simple sans méthodes métiers, comme un Account ou un User par exemple). Indépendant du lieu de stockage (SGBDO ou SGBDR) et fonctionnant sans conteneur d’exécution.
JDO utilise un mécanisme d’enrichisseur de byte-code pour attraper les class en cours d’exécution et les persister vers un lieu de stockage. JDO2 en cours de rédaction devrait combler les limitations de JDO qui sont la gestion des transactions et la sécurité.
Concurrent direct de JDO, Hibernate est un projet open-source intégré comme moteur de persistance dans Jboss. D’ailleurs les développeurs d’Hibernate ont rejoins Jboss Inc. et certains sont salariés par l’entreprise privée Jboss Inc. Très facile à mettre en œuvre dans un serveur J2EE ou dans une application Java classique, Hibernate est un moteur de mapping objet-relationnel. Ce système permet d’avoir en mémoire par exemple une class Person mappée à partir d’une base de données. Hibernate est à la base du principe de persistance des EJB3.
Le pattern Inversion Of Control a été mis en place par quelques frameworks comme Spring par exemple. Le principe de l’inversion de control est très simple et repose sur la réflection en Java. Dans l’exemple que nous avons vu, voici le code que nous avons:
public class Product{ private ProductDAO dao; public Product() { dao = (ProductDAO)DAOFactory.getProductDAO(); } .. }
La class Product récupère une class d’accès aux données sans en connaitre l’implémentation. Imaginons que nous avons codé 2 class implémentant l’interface ProductDAO, une pour Oracle et une autre pour Sybase. La factory retournerait alors la bonne implémentation selon un paramètre de configuration par exemple.
En utilisant l’inversion de contrôle, le code de la class Product sera par exemple:
public class Product { private ProductDAO dao; public Product() {} public void setProductDAO(ProductDAO dao) { this.dao=dao; } … }
Il faut maintant un moteur spécial (comme Spring) pour injecter à l’execution la bonne class implémentant ProductDAO. Pour cela, nous allons configurer la class dans un fichier XML pour que Spring place la bonne classe.
<beans> <bean id="OracleProductDAOBean" class="com.reuters.dao.OracleProductDAO"/> <bean id="TestNic" class="com.reuters.product.Product"> <property name="productDAO"> <ref bean="OracleProductDAOBean"/> </property> </bean> … </beans>
Ce fonctionnement simplifie la programmation et permet aussi de définir des mock-object pour des tests unitaires très facilement.
Les annotations sont aussi un nouveau mécanisme inspiré au départ de Xdoclet. L’idée est que lors de la phase de dévelopement, le programmeur spécifie à l’aide de tag spéciaux les fonctions de sa class ou de sa méthode. C’est l’une des grandes nouveautés de Java 5. Les annotations permettent alors de déclencher des actions génériques automatiquement sans trop surcharger le code.
La programmation par aspect (AOP) est encore relativement jeune. Il s’agit d’alléger le code en déclenchant des aspects automatiquement lorsque certains critères sont rencontrés. Imaginez que vous ayez un aspect LogInfo qui affiche un message d’information. A l’aide d’un moteur d’aspect vous pouvez par exemple demander d’appliquer cet aspect sur chaque constructeur de votre code. Dès qu’un objet est construit, un message est généré. Facilité de maintenance car il n’y aura pas de log.info(« qqq ») dans vos constructeurs. Souplesse de programmation car il est possible d’ajouter et retirer ces aspects sans recompiler le code. Centralisation aussi des séquences de traitement répétitives…. On verra avec les EJB3 des cas concrets d’aspect ainsi que leur puissance.
A ce stade nous avons donc vu qu’il existe des outils puissants pour répondre aux lacunes des EJB2.1. Cependant il n’y a pas de normalisation et une phase d’apprentissage puis d’intégration est nécessaire pour faire fonctionner toutes ces technologies Java ensemble.
3) Les EJB3
Dans la deuxième partie nous verrons comment écrire un EJB3 Session, développer un entité, le fonctionnement de l’Entity Manager, la vue du côté client, les annotations avancés. Suite à la présentation j’ai essayé aussi de regarder où en était BEA, IBM et SUN par rapport à Jboss, qui, pour l’instant, est le seul serveur J2EE prêt pour les EJB3
Où en sommes-nous ?
Tout d’abord avant de continuer sur le résumé de la présentation Valtech, j’ai regardé sur le site Java Community Process l’historique des spécifications EJB3 pour voir où nous en sommes. Ce qui a été décidé: EJB3 sera partie intégrante de J2EE 5.0 actuellement en phase de Final Draft Version. La sortie officielle est donc prévue courant 2006.
2 objectifs pour la nouvelle spéc des EJB3:
- Simplification du développement
- Standardisation de l’API Java Persistence
Tout d’abord concernant la simplification du développement, les EJB3 retirent le besoin d’implémenter une interface spécifique pour les interfaces Home ainsi que les Beans eux-mêmes. Les SessionBeans, les Entity et les MDB sont donc maintenant de simples class Java auxquelles nous allons ajouter des fonctionalités Business. Les EntityBeans sont maintenant des class java standards avec un design à respecter (Serializable, constructeur public sans args, annoté avec le tag @Entity, etc.)
EJB3 intègre le meilleur des alternatives que nous avons présenté dans la partie 1. Le modèle d’Hibernate a inspiré l’API Java Persistence (EJB3 n’est pas Hibernate). Les annotations permettent d’éviter les fichiers XML compliqués (à la Xdoclet). Le couple IoC/annotation permet au conteneur de binder à l’execution la valeur d’une class. Ceci permet d’éviter d’avoir à faire des lookup JNDI. Il suffit de dire que vous avez besoin de la Datasource OracleTest pour que celle-ci soit branchée par le containeur à l’exécution.
Développer un Session Bean
Un Session Bean est une class Java simple qui peut implémenter une interface SessionBean sans que ceci soit nécessaire. Comment alors le containeur sait qu’il s’agit d’un sessionBean ? La réponse est: les annotations. Et donc si nous avons des annotations cela signifie que nous utilisons Java 5. EJB3 utilise Java 5 et ne fonctionnera donc pas avec les anciennes versions. Sans se lancer dans le débat pour/contre, il faut voir que Java 5 est vraiment une version à part de Java bien plus riche en terme de fonctionnalités que Java 1.4. Cette migration est donc sans douleur, le code Java écrit en 1.3 et 1.4 étant évidemment compatible avec Java 5.
Pour revenir à la class SessionBean, en utilisant une annotation nous aurons donc le code suivant:
/** * Demo Reuters EJB3.0 Nicolas Martignole */ @Stateless public class CourseManagerBean implements CourseManager { public void create(Course c) { ... } public Course findCourseById(String courseId) { … } public CollectionfindAll(){ // return all courses. } }
L’annotation Stateless (case-sensitive) signale au conteneur qu’il s’agit d’un Stateless Session Bean.
Voyons maintenant les interfaces
Interface remote:
@Remote interface CourseManagerRemote extends CourseManager { }
Interface locale:
@Local interface CourseManagerLocal extends CourseManager { }
L’utilisation de ces 2 annotations est optionnelle. Elle permet d’outrepasser le comportement par défaut du « compilateur » en mentionnant de façon explicite le nom des interfaces distante et locale de cet EJB. La version draft des spécifications EJB évoque aussi l’éventualité de n’avoir pas besoin d’écrire soi même l’interface métier mais de laisser cette tâche au conteneur. Les méthodes publiques et non « injectées » seraient alors considerées comme accessible via l’interface métier. Imaginez le temps que vous avez perdu dans vos ejb-truc.xml à y placer le nom des méthodes… Ca y est vous voyez ? Le code reprend l’ownership sur la logique. Si une méthode est publique, à priori c’est que nous voulons nous en servir dans notre EJB. Logique non ?
Comment est bindé ce SSB via JNDI ? Comment le retrouver ?
Ici un apport d’EJB3, pas évident à expliquer mais vraiment « tueur », est que les références se basent sur les class Java.
Le client retrouvera une instance de ce CourseManagerBean de cette façon:
InitialContext ctx=new InitialContext(); CourseManager cm=(CourseManager)ctx.lookup(CourseManager.class.getName()); cm.create(myCourse); cm.findCourseById("nicolasTest");
Simple non? (je commence à passer en mode « évangélisation » , désolé )
Mais cela sera encore plus simple, car si nous utilisons le pattern IoC il est possible de demander dans son code « donne moi une instance de CourseManager » de la façon suivante:
@EJB CourseManager myCm; // Sera bindé par le containeur à la volée myCm.create(testCourse);… myCm.findCourseById("nicolasTest")";
Voilà pour un petit aperçu de ce qu’il sera possible de faire via les EJB3 concernant les Sessions Bean. Il y a de nombreuses fonctionnalités (sécurité, transaction) ainsi que le support pour retrouver.
Développer un Entity Bean
La gestion de la persistance est sans doute la plus grande révolution des EJB3. A elle seule elle peut couvrir plusieurs livres.
Pour commencer un EJB Entity est un object POJO (Plain Old Java Object) ou plus clairement, une class qui n’a rien de particulier. Pour définir notre entity Course utilisée ci-dessus voici comment nous devons la déclarer:
/** * EJB3 Entity Bean that represents a Course. *@author Nicolas */ @Entity @Table(name="CourseTable", schema="Sybase_KiwiSample") public class Course implements Serializable { private String description; private String id; /* Public no-arg constructor (required by spec) */ public Course{} @Id public String getId() { return id; } public void setId(String id){ this.id=id; } public String getDescription() { return description; } public void setDescription(String desc){ this.description=desc; } }
3 annotations permettent de transformer cette class simple en un entity bean qui sera persisté automatiquement sur une base de données.
@Entity est une annotation marqueur pour le containeur
@Table est le nom de la table en base qui va mapper cet Entity.
Si la table n’existe pas ? le containeur va la créer automatiquement
Si je change le type d’un attribut (Description devient un Integer) -> le containeur se débrouille tout seul.
Comment mapper l’Entity Bean sur plusieurs tables de la base de données ?
Voici comment procéder:
/** * EJB3 Entity Bean that represents a Course. *@author Nicolas */ @Entity @Table(name="CourseTable", schema="Sybase_KiwiSample") @SecondaryTable(name="CustId") @JoinColumn(name="CustId") public class Course implements Serializable { private String description; private String id; /* Public no-arg constructor (required by spec) */ public Course{} @Id public String getId() { return id; } public void setId(String id){ this.id=id; } public String getDescription() { return description; } public void setDescription(String desc){ this.description=desc; } }
Si un attribut de ma class ne porte pas le même nom que ma colonne en base ?
Pas de soucis:
@Columnd(name="P_DESC") public String getDescription() { return description; }
Il existe d’autres annotations pour résoudre les problèmes liés au mapping, spécialement si vous partez d’une base de données existantes.
Qu’est-ce que le tag @Id ?
Directement inspiré d’Hibernate, il s’agit d’une annotation pour marquer le nom du champ qui sera la clé discriminante pour l’objet.
Concernant les Collections, je ne sais pas comment est fait le mapping. Aucunes idées. Je sais qu’avec Hibernate c’est faisable via un fichier de configuration particulier.
Quoi d’autre ?
Il y a aussi la gestion de l’héritage et des relations en Java. Si un Entity Course a une relation de many-to-one avec un Entity Teacher, EJB3 est capable de gérer les mappings correctement. C’est vraiment puissant et simple car d’un point de vue développeur, tout est fait en Java directement. Votre class Course aura une instance de Teacher et la class Teacher aura une List de Course. Le mapping objet/relationnel est effectué par le containeur.
Les performances?
Difficile pour l’instant à évaluer. Cependant la gestion de la persistance par le containeur permettra d’optimiser les appels en mémoire par rapport à la base. En terme de testabilité il est clair qu’un simple objet Java sera plus facile à tester qu’un objet héritant des interfaces J2EE.
L’EntityManager
L’API EntityManager a été introduite dans EJB3.0 et permet aux instances des objets Java d’être détaché et chargé localement pour être ensuite renvoyé au serveur et persisté dans la base de données.
La « home » des EJB Entités disparaît pour laisser place à l’Entity Manager qui représente si vous voulez une « home » générique.
Permet de créer un EJB, de rechercher par clé primaire, d’executer une requête EJBQL et vérifier l’existence d’une clé primaire.
Pour essayer d’expliquer cela, voici un petit exemple:
@Stateless public class CourseManagerBean implements CourseManager{ @PersistenceContext private EntityManager mgr; // Save the Course to the persistence layer public void create(Course c){ mgr.persist(c); } public Course findByPrimaryKey(String courseId) { return mgr.find(Course.class, courseId); } public Collection findAll(){ return mgr.createQuery("select c from Course c").listResults(); } }
L’annotation @Stateless marque cette class comme étant un Stateless Session Bean.
L’annotation @PersistenceContext déclare une instance d’EntityManager. Via le pattern IOC le containeur injectera à l’execution une référence vers une instance d’EntityManager.
La méthode create enregistre un nouveau objet Course (création d’une ligne dans la base de données, éventuellement création de la table si celle-ci n’existait pas)
La méthode findByPrimaryKey permet de rechercher un objet de type Course qui matche
tel courseId.
La méthode findAll montre comment créer une requête EJBQL. Celle-ci est simplifiable mais je n’ai pas noté. Je crois que c’est « from Course c » pour faire un select * par défaut.
Nous avons vu ensuite le langage de requetage d’EJB ainsi que la vue Client des EJB3. C’est assez détaillé et je ne reprendrai pas ici toute la présentation.
A ce stade vous devez avoir déjà une idée de ce qu’est réellement la spécification des EJB3.
Quels sont les inconvénients visibles pour l’instant?
- EJB3 se repose sur Java 5, ce qui peut être un facteur bloquant pour certains clients même si ce n’est pas justifié
- Les annotations ne doivent pas être plus nombreuses que le code lui-même
- Il apparaît que des conventions de code pour que le code soit homogène et propre dans une équipe. Sinon les annotations seront placées n’importe où. Pas forcément facile à mettre en place.
Où en sont les principaux acteurs du marché ?
Jboss: le serveur d’application Jboss est pour l’instant le seul serveur Java prêt pour les EJB3 qui fonctionne parfaitement depuis quelques mois. C’est un énorme avantage compétitif qui risque certainement de mettre en difficulté d’autres projets open-source
GlassFish, un projet open-source hébergé sur Java.net est une initiative pour créer le premier serveur compatible JEE5 (on ne dit plus J2EE mais JEE…)
Home-page: https://glassfish.dev.java.net/
Abstract:
The GlassFish community is building a free, open source application server which implements the newest features in the Java EE 5 platform (the next version of the J2EE platform). The Java EE 5 platform includes the latest versions of technologies such as Enterprise JavaBeans (EJB) 3.0, JavaServer Faces (JSF) 1.2, Servlet 2.5, JavaServer Pages (JSP) 2.1, Java API for Web Services (JAX-WS) 2.0, Java Architecture for XML Binding (JAXB) 2.0, Java Persistence 1.0, Common Annotations 1.0, Streaming API for XML (StAX) 1.0, and many other new technologies.
IBM a annoncé le rachat le 10 mai 2005 dernier de GlueCode, l’équipe des développeurs à l’origine de Geronimo (serveur J2EE open-source). Une analyse Garnet (http://www.gartner.com/DisplayDocument?doc_cd=127754) explique qu’IBM veut freiner la montée de Jboss et tester aussi ce nouveau business model « subscription-based software pricing » où le client vient acheter du support, de l’expertise technique et de l’aide.
Parmis les risques identifiés, Geronimo n’est pas encore certifié J2EE comme l’est Jboss ou BEA Weblogic
IBM Websphere 7.0 (Vela) devrait sortir prochainement après la sortie de la 6.0 en décembre 2004. Aucunes idées si les EJB3 seront intégrées dans cette version.
BEA a participé à la rédaction des spécifications EJB3.0 et a sorti mi-nopvembre 2005 Weblogic Server 9.0 beta pour tester.
Site de BEA
BEA a aussi annoncé le rachat de SolarMetrics Inc. (Communiqué de presse en français) pour conforter et certainement acquérir des compétences EJB3.0. SolarMetrics propose Kodo, un moteur de persistance compatible à la fois EJB3.0 et à la fois JDO.
Donc c’est en préparation et BEA sortira une pré-version début 2006.
Conclusion
Les EJB3 apportent de bonnes solutions sur les points décriés tels que le modèle de programmation, le modèle composant et la mauvaise gestion de la persistance.
Les EJB évoluent vers une version plus « mature » et cette nouvelle spécification prend en compte aussi les applications réellement réalisées depuis quelques années. Le modèle distribué est peu utilisé, la gestion des ressources restaient trop spécifiques aux containers.
Nous avons vu aussi au passage la puissance du serveur d’application Jboss. Ce n’est pas un simple conteneur J2EE mais un kernel serveur qui permet dès aujourd’hui de coder des EJB3.0 sans trop d’effort.
Voilà pour le résumé. Je parlerai prochainement de JBoss Seam, une application qui propose JBoss AS + EJB3 + JSF (MyFaces) le tout prêt à l’emploi.
0 no like