Une introduction `a la technologie EJB
Transcription
Une introduction `a la technologie EJB
Une introduction à la technologie EJB 1 Introduction La technologie des EJB (pour Enterprise Java Bean) ont été introduite en 1998 pour offrir aux programmeurs un outil qui facilite la conception et le déploiement d’une couche métier. La version 1.0 très simple est largement revue en 2002 avec la version 2.0. Cette dernière introduit de nombreuses nouveautés avec la possibilité de travailler directement (via des EJB particuliers) sur des données stockées dans une base de données relationnelle. Malheureusement, ces nouvelles fonctions s’accompagnent d’une lourdeur très importante de mise en oeuvre. Les composants EJB 2.0 doivent implémenter de multiples interfaces et leur déploiement impose de fournir au serveur d’applications des fichiers de description fastidieux rédigés en XML. En 2006, la version EJB 3.0 a pour objectif de simplifier l’utilisation et le déploiement de cette technologie en se basant sur quatre principes : • Les fichiers XML de déploiement sont remplacés par des annotations placées directement dans le code des EJB. Cette nouvelle possibilité est directement liée aux apports de la version 1.5 de JavaSE (définition et exploitation des annotations). • Pour assurer les interactions entre les EJB et le serveur d’application dans lequel ils sont installés, le programmeur n’est plus obligé d’implémenter de nombreuses interfaces (au sens java du terme). Ici encore, les annotations vont simplifier les choses. • Tous les paramètres de déploiement sont dotés de valeurs par défaut qui suffissent dans la plupart des cas. • Les mouvements de données vers ou à partir d’une base de données relationnelle vont être réalisés par le biais de POJO (Plain Old Java Object). Ces composants très simples vont remplacer les EJB Entités de la version 2.0 et ainsi simplifier le travail du programmeur. Il faut noter que là également, les annotations appliquées aux POJO vont permettre une mise en oeuvre plus simple. Il existe trois types d’EJB : • Les EJB Session fournissent un service aux clients. Ils sont accessibles via le réseau (en passant par le protocole RMI) ou localement dans la JVM du client. Les EJB sessions sont les points d’entrée de la couche métier. • Les EJB Entity représentent les données qui proviennent ou qui alimentent la base de données. Ils ne sont généralement pas accessibles directement au client. Ce dernier doit, traditionnellement, passer par un EJB session pour récupérer des EJB entités. • Les EJB MessageDriven représentent une file de messages postés par le client et traités par le serveur (ou vice-versa). Nous ne les étudierons pas dans ce TP. Quelques liens : Javadoc de l’API EJB 3.0 http://docs.oracle.com/javaee/6/api/index.html?javax/ejb/package-summ Le tutorial JEE sur les EJB http://docs.oracle.com/javaee/6/tutorial/doc/bnblr.html 2 Mise en place Pour travailler avec les EJB en version 3.0, nous avons besoin de mettre en place des outils supplémentaires : IDE Eclipse JEE (à sauter si déjà fait) Commencez par charger la version JEE de l’IDE Eclipse 1 . Nous avons besoin de cette version pour utiliser les nombreux plugins qu’elle intègre. 1. http://www.eclipse.org/downloads/ 1 OpenEJB Téléchargez OpenEJB 2 en version standalone (aussi disponible ici 3 ). OpenEJB est un conteneur d’EJB qui est à la fois assez léger et utilisable sous la forme d’une librairie (serveur embarqué). Décompressez l’archive obtenue. Plugin OpenEJB for Eclipse Nous allons également installer un plugin afin de gérer le déploiement du serveur OpenEJB. Utilisez le menu Preferences/Install-Update/Add... et ajoutez le site http://people.apache.org/~jgallimore/update-site/ Suivez ensuite ces étapes : • Dans Eclipse, assurez-vous que la vue Servers est active. • Créez un nouvel environnement d’exécution (Runtime Environments) basé sur Apache OpenEJB (menu Preferences/Server/Runtime Environments/Add...) • Créez un projet de type EJB et basez le à sa création sur l’environement d’exécution OpenEJB. • Créez ensuite (dans la vue Servers) un nouveau server pour exécuter cette application. 3 Les EJB Session La réalisation d’un EJB Session requière deux étapes. Dans un premier temps, nous devons rédiger le cahier des charges sous la forme d’une interface, et dans un deuxième temps, nous devons fournir une implantation. Nous retrouvons donc les concepts déjà présentés dans la technologie RMI. Il existe deux types d’EJB Session : Stateless Session Bean Comme leur nom l’indique, ces EJB Session n’ont pas d’état. En d’autres termes, il existe sur le serveur d’applications une ou plusieurs instances de ces EJB qui sont utilisées par tous les clients de manière simultanée. En d’autres termes, il n’existe pas de liens entre les instances côté serveur et les clients. Ces EJB sont souvent utilisés comme guichet unique d’une couche métier. Stateful Session Bean Contrairement aux Stateless, les Stateful possèdent un état qui reflète l’état de la discussion avec un client particulier. En d’autres termes, il existe dans le S.A. autant d’instances que de clients connectés au serveur. Les instances d’un EJB Session Stateful sont donc spécifiques à un client. Ces EJB sont souvent utilisés pour représenter un utilisateur connecté. 3.1 Un Stateless Session Bean Nous allons commencer par construire un petit EJB Session Stateless dont le but est de passer une chaı̂ne de caractères en majuscule. Nous commencons par définir l’interface : package monpkg.services; import javax.ejb.Remote; @Remote public interface ToUpper { String toUpper(String data); } 2. http://openejb.apache.org/ 3. ref:ress-ejb 2 Dans cette interface, l’annotation javax.ejb.Remote 4 est utilisée pour préciser que l’EJB sera accessible via le protocole RMI. Si l’EJB est implanté dans la même JVM, nous aurions du utiliser l’annotation javax.ejb.Local 5 . Un EJB peut être à la fois distant et local. Il faut dans ce cas deux interfaces. puis nous réalisons une implantation : package monpkg.impl; import javax.ejb.Stateless; import monpkg.services.ToUpper; @Stateless(name = "toUpper", description = "Un premier essai") public class ToUpperImpl implements ToUpper { public String toUpper(String qui) { return qui.toUpperCase(); } } L’annotation javax.ejb.Stateless 6 permet de typer votre EJB et de lui donner un nom. A ce stade vous pouvez demander un synchronisation de votre serveur (opération qui déploie le JAR de votre projet dans le serveur d’applications). Vous pouvez lancer votre serveur, observer les traces (nombreuses) et identifier le déploiement de votre EJB. 3.2 Mise en place du client distant (remote) Nous allons maintenant créer le client sous la forme d’un test unitaire dans un nouveau projet java standard (pas EJB). Mettez en place une dépendance entre le projet client et le projet EJB. 4. http ://docs.oracle.com/javaee/6/api/ ?javax/ejb/Remote.html 5. http ://docs.oracle.com/javaee/6/api/ ?javax/ejb/Local.html 6. http ://docs.oracle.com/javaee/6/api/ ?javax/ejb/Stateless.html 3 package monclient; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import monpkg.services.ToUpper; import org.junit.Assert; import org.junit.Test; public class Client { @Test public void testRemoteToUpper() throws NamingException { // prepare client context Properties props = new Properties(); String client = "org.apache.openejb.client.RemoteInitialContextFactory"; props.put(Context.INITIAL_CONTEXT_FACTORY, client); props.put(Context.PROVIDER_URL, "ejbd://localhost:4201"); Context context = new InitialContext(props); // lookup EJB Object ref = context.lookup("toUpperRemote"); // test and use it Assert.assertTrue(ref instanceof ToUpper); ToUpper p = (ToUpper) ref; Assert.assertEquals("PREMIER", p.toUpper("premier")); } } Pour éviter de préciser dans le code les paramètres de connexion JNDI, vous pouvez créer un fichier de ressources « jndi.properties » accessible via le CLASSPATH : java.naming.factory.initial=org.apache.openejb.client.RemoteInitialContextFactory java.naming.provider.url=ejbd://localhost:4201 Le nom que nous utilisons pour repérer un EJB est de la forme nomRemote si l’EJB est accessible via RMI ou nomLocal si l’EJB est disponible dans la même JVM. Si vos EJB sont packagés dans une application d’entreprise (une EAR : Enterprise Application aRchive), alors le chemin devient nom-de-EAR/nomRemote ou nom-de-EAR/nomLocal. Une archive EAR regroupe des EJB (packagés dans un .jar) et une application WEB (packagé dans un .war). Vous pouvez, dans Eclipse, créer une application d’entreprise pour regrouper un ensemble d’EJB et une application WEB. Testez votre application puis stoppez le serveur. 3.3 Mise en place du client local Pour faciliter nos tests, nous allons les exécuter dans la même JVM que nos EJB sous la forme de test unitaires. Le code devient 4 package monclient; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import monpkg.services.ToUpper; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; public class Client { @Test public void testLocalToUpper() throws NamingException { // prepare client context Context context = new InitialContext(); // lookup EJB Object ref = context.lookup("toUpperRemote"); // test and use it Assert.assertTrue(ref instanceof ToUpper); ToUpper p = (ToUpper) ref; Assert.assertEquals("PREMIER", p.toUpper("premier")); } } Avec le fichier « jndi.properties » suivant : java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory 3.4 EJB locaux Si l’accès distant n’a pas d’importance nous pouvons déclarer notre EJB comme uniquement local : package monpkg.impl; import javax.ejb.Stateless; import javax.ejb.Local; import monpkg.services.ToUpper; @Stateless(name = "toUpper", description = "Un premier essai") @Local public class ToUpperImpl implements ToUpper { public String toUpper(String qui) { return qui.toUpperCase(); } } Dans ce cas, il faudra utiliser le nom toUpperLocal côté client. Testez cette fonction. 5 3.5 Cycle de vie d’un EJB Un EJB a souvent besoin d’être informé (par le serveur d’applications) de sa création et de sa destruction. Dans les EJB2, de nombreuses interfaces étaient prévues pour assurer cette fonction. Dans les EJB3, il suffit d’annoter certaines méthodes pour qu’elles soient appelées par le S.A. @PostConstruct() public void debut() { System.out.println("Starting " + this); } @PreDestroy public void fin() { System.out.println("Stopping " + this); } Ajoutez ces callbacks à votre EJB et testez leur fonctionnement. Vous pouvez remarquer que ces annotations 7 ne sont pas directement liées à la technologie des EJB3. 3.6 Synchronisation de l’accès aux EJB Pour une instance donnée d’un EJB les accès aux méthodes sont automatiquement synchronisés par le serveur d’applications. Cette limitation garantie q’une action métier ne puisse pas entrer en conflit avec une autre action métier du même EJB. Travail à faire : • Ajoutez à votre EJB un travail long de 2 secondes (modifiez l’interface puis l’implantation). • Créez un test unitaire qui exécute cette méthode sur la même instance mais dans deux threads (inspirez-vous du code ci-dessous pour la création d’un thread). Le test consiste à vérifier le temps d’exécution. Runnable codeThreadFils = new Runnable() { public void run() { // code du thread fils } }; // code du thread père Thread t = new Thread(codeThreadFils); t.start(); // demarrage du fils t.join(); // attente de la mort du fils • Créez ensuite un autre test unitaire qui exécute cette méthode dans deux threads chacun ayant sa propre instance. Le test consiste à vérifier le temps d’exécution. • Combien de threads sont-ils créés dans le serveur d’applications pour assurer le service aux clients ? 3.7 Un EJB Session Stateful Essayons maintenant de construire un Stateful Session Bean. Nous allons utiliser le cas classique d’un utilisateur connecté. Commencons par définir l’interface : 7. http://docs.oracle.com/javaee/6/api/javax/annotation/package-summary.html 6 package monpkg.services; import javax.ejb.Remote; @Remote public interface ConnectedUser { void login(String login, String pwd); void logout(); } Puis l’implantation : package monpkg.impl; import javax.annotation.PostConstruct; @Stateful(name="connectedUser") public class ConnectedUserBean implements ConnectedUser { // implantation des méthodes ... } L’annotation javax.ejb.Stateful 8 indique au serveur d’application qu’il doit créer une instance de cet EJB pour chaque client. Vérifiez que c’est bien le cas avec des traces générées par des méthodes annotées par @PostConstruct. Normalement le client doit être capable de terminer un EJB Stateful. Pour ce faire, vous devez annoter les méthodes qui terminent ce bean avec @Remove. Le bean sera détruit après avoir exécuté la méthode. @Remove public void logout() { System.out.println("Logout user "+this); } Travail à faire : • Utilisez ces principes pour authentifier et déconnecter un utilisateur. • Vérifiez les cycles de vie du Stateful Session Bean avec une méthode annotée par @PreDestroy. • Essayez d’utiliser une instance après l’avoir détruite. • Prenez soin de créer plusieurs clients et de vérifier que chaque client à sa propre instance de l’EJB. Pour ce faire, associez une propriété nom à chaque utilisateur connecté. 4 Injection d’EJB Nous avons souvent besoin, dans une application, d’utiliser un premier EJB dans l’implantation d’un second. Pour ce faire, nous devons faire un lien entre ces deux EJB. Nous pouvons toujours le faire en considérant l’EJB utilisateur comme un client et donc passer par un lookup JNDI. Pour simplifier ces problèmes de liaison, la norme EJB3 introduit la notion d’injection de dépendances via l’annotation javax.ejb.EJB 9 : 8. http ://docs.oracle.com/javaee/6/api/ ?javax/ejb/Stateful.html 9. http ://docs.oracle.com/javaee/6/api/ ?javax/ejb/EJB.html 7 @EJB private ToUpper toUpper; En ajoutant ce code au bean ConnectedUserBean nous sommes capable d’utiliser directement l’EJB toUpper dans l’implantation. Les injections sont réalisées après la création et avant l’appel des callbacks @PostConstruct. Pour mettre en oeuvre ce principe, créez un Stateless Session Bean qui offre un service de log et injectez cet EJB dans votre utilisateur connecté pour suivre les créations, authentifications et destructions. 5 EJB Embarqués De nombreuses applications veulent utiliser les avantages des EJBs sans avoir à déployer un serveur d’applications. Pour ce faire, on peut utiliser un serveur d’applications embarqué qui se présente sous la forme d’une librairie Java : Testez ces exemples : • http://openejb.apache.org/examples-trunk/simple-stateless/README.html • http://openejb.apache.org/examples-trunk/simple-stateful/README.html Remarque 1 : Dans ces exemples, vous utilisez la version 3.1 des EJB dans laquelle les interfaces sont devenues optionnelles. Remarque 2 : Dans ces exemples, vous utilisez la nouvelle façon d’accéder aux EJB via la classe javax.ejb.embeddable.E (EJB 3.1) Remarque 3 : Je vous encourage à créer des projets Java Classiques et à utiliser Maven pour charger la librairie OpenEJB 11 . 6 Tout mettre ensemble ! L’objet de cet exercice est de construire une application d’entreprise qui regroupe des composants métier (EJB) et des composants Web (Servlet/JSP). Suivez les étapes soigneusement : 1. Téléchargez le serveur d’applications TomEE 12 (disponible ici 13 ) et décompressez l’archive. C’est un assemblage de Tomcat et OpenEJB. 2. Lancez Eclipse version JEE. 3. Ajoutez un nouveau runtime de type Apache Tomcat 7.x (menu Preferences/server/runtime/add...), mais donnez le répertoire de TomEE à la place de celui de Tomcat. En clair, nous allons déployer TomEE comme une instance de Tomcat. 4. Ajoutez un nouveau serveur basé sur ce runtime. 5. Créez une application Dynamic WEB « myapp » basée sur le serveur précédent. 6. Créez dans ce projet un EJB Stateless ci-dessous : 11. http://mvnrepository.com/artifact/org.apache.openejb/openejb-core 12. http://tomee.apache.org/apache-tomee.html 13. ress-ejb 8 package myapp.beans; import javax.ejb.LocalBean; import javax.ejb.Stateless; @Stateless(name = "Hello") @LocalBean() public class Hello { public String sayHello() { return "Hello " + System.currentTimeMillis(); } } Nous n’utiliserons pas d’interface (EJB local en version 3.1). 7. Créez la servlet ci-dessous : package myapp.web; import java.io.IOException; import import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; import myapp.beans.Hello; @WebServlet( name = "hello", displayName = "My JEE 6 Servlet", description = "Ma première servlet avec annotations", loadOnStartup = 10, urlPatterns = { "/Hello1", "/Hello2" } ) public class HelloServlet extends HttpServlet { private static final long serialVersionUID = 1L; @EJB(name = "Hello") Hello hello; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String data = hello.sayHello(); response.getWriter().printf("<p>%s</p>", data); } } 8. Exécutez l’application d’entreprise myapp sur le serveur TomEE et testez votre application WEB avec l’adresse ci-dessous : http://localhost:8080/myapp/Hello2 9. Vous venez de construire une application autonome prête à être déployée sur une serveur d’applications JEE. Vous pouvez lui adjoindre d’autres composants métier (EJB). 9
Documents pareils
TP EJB - Objis
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.objis.ejb3.session.MonEjb3Session;
public class ClientMonEjb3Session {
public static void main(String[] args) {
t...