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

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...

Plus en détail