You are on page 1of 74

EJB 3

formation interne pour CLIO S.A.

Cédric BOTTERO
Michaël MATHIEU

Genève, le 30 juin 2009


Plan
• Introduction
• 3 types d’EJB (Session, Entity, Message-Driven)
• Injection et JNDI
• Intercepteurs et callbacks
• Transactions (CMT, BMT)
• Sécurité
• Persistance avec JPA
Introduction
• Permet d’éviter au développeur d’avoir à se
préoccuper de tout ce qui a trait au système :
– Transactions
– Sécurité
– Evolutivité
– Concurrence
– Communications
– Gestion des ressources
– Persistance
– Gestion des erreurs
– Etc.
3 types d’EJB
• Session
– Stateless
– Stateful
• Entity (depuis la version 3, est un simple POJO)
• Message-Driven
Session
• Sont conçus pour encapsuler la logique métier
• Les plus utilisés
• 2 types d’EJB session :
– Stateless
– Stateful
Session -> Stateless
• Stateless (sans état) => les attributs de l’EJB sont réinitialisées
entre chaque appel même s’il s’agit du même client
• Sont spécialement pensés pour être robustes et fiables lorsqu’il y
a beaucoup d’appels en concurrence

• Lorsqu’un client appelle l’EJB, une instance de ce dernier sert le


client, puis, retourne dans le pool d’EJB (cette dernière est donc
prête a être réutilisée pour un autre client)
• A utiliser le plus souvent possible (par rapport aux Stateful) =>
cycle de vie
EJB 2 vs. 3
• Pour les EJB session stateless, nous commencerons
par présenter leur utilisation avec les EJB 2, puis
avec les 3
• Le but est de montrer les améliorations et
simplifications amenées avec la version 3
• Cela permettra également de mieux comprendre le
principe de fonctionnement des EJB (par exemple,
l’utilisation du
javax.rmi.PortableRemoteObject.nar
row(…))
Structure d’un EJB
• Avec les EJB 2, la configuration des EJB se fait dans
le fichier META-INF/ejb-jar.xml
• Avec les EJB 3, le fichier de configuration n’est plus
nécessaire: la configuration se fait dans le code au
moyen des annotations
Session -> Stateless
EJB 2
Session -> Stateless
EJB 2

• Par convention, et ce afin d’éviter des


malentendus, les méthodes métier ne doivent pas
commencer par « ejb »
Session -> Stateless
EJB 2
META-INF/ejb-jar.xml
Session -> Stateless
EJB 2
Session -> Stateless
EJB 3
Session -> Stateless
EJB 3
Session -> Stateful
• Stateful (avec état) => les attributs de l’EJB sont sauvegardés durant toute
la session

• Lorsqu’un client appelle l’EJB, une instance de ce dernier est créée, puis
sert le client. Cette instance reste disponible pour les futurs appels de ce
client uniquement. Cette instance sera détruite à la fin de la session
(timeout ou appel à une méthode portant l’annotation @Remove)
• S’il y a trop d’instances d’un EJB en mémoire, ces dernières peuvent être
sorties de la mémoire de travail. Elles passent ainsi en mode passif (=
sauvées sur disque => tous les attributs doivent être sérialisables = types
implémentant l’interface Serializable)
Sérialisation
• Le type des attributs et le type de retour de chaque méthode doivent être
Serializable (sauf dans le cas d’une méthode d’un EJB stateless local)
• Par précaution, nous tâcherons de veiller à ce que tous les types que nous
utilisons soient Serializable
• Le serialVersionUID permet de faire la distinction entre des objets de
même type, mais d’une version différente
• Lorsque qu’un objet est reçu, le destinataire vérifie que le serialVersionUID
de l’objet reçu correspond à celui de la classe qu’il a chargé. S’il diffère, une
InvalidClassException est lancée
• Ce mécanisme permet de s’assurer que l’émetteur et le destinataire travaillent
avec la même version de l’objet
• Cette solution a une faiblesse: lors d’une modification, elle nécessite que toutes
les applications qui utilisent cette classe doivent être mis à jour, alors que cela
n’est pas forcément nécessaire. En effet, dans le cas où on ajoute un nouvel
attribut à un objet, cela ne signifie pas forcément que toutes les applications ont
l’utilité de ce nouvel attribut…
=> on donne une valeur arbitraire par défaut au serialVersionUID et on ne
la modifie pas
Local vs. Remote
• Permet de spécifier la manière d’accéder à l’EJB =>
l’implémentation des services est identique
• Local
≠ appel à un EJB se trouvant sur la même machine
= appel à un EJB se trouvant dans la même JVM
• Remote
– utilise RMI (Remote Method Invocation) (=> plus lent
qu’un appel local) et JNDI (Java Naming and Directory
Interface)
– utilisé dans la majorité des cas
Local
EJB 2
Local
EJB 2
Local
EJB 2
META-INF/jboss.xml
Local
EJB 2
Local
EJB 2
META-INF/jboss.xml
Local
EJB 2
Local
EJB 3
Entity
• Permet de gérer la persistance comme le ferait
Hibernate sur le concept de object-relational
mapping (ORM) => illusion de travailler avec une
base de données objet
• Le mapping ne se fait plus forcément dans un
fichier XML (comme hibernate.cfg.xml),
mais directement dans le code avec des
annotations (@Id, @Column, etc.)
• D’avantage de détails dans le dernier chapitre de
ce cours « Persistance avec JPA »
Message-Driven
• Permet à des applications de communiquer entre elles, en étant faiblement
couplées, et de manière asynchrones
• Ce concept est connu sous le nom de Message-oriented middleware (MOM)
• Les produits implémentant ce mécanisme sont nombreux comme IBM
WebSphere MQ (anciennement MQSeries), JBoss Messaging (qui remplacera
JBoss MQ), etc.

PTP (point-to-point) pub-sub (publish-subscribe)


javax.jms.Queue javax.jms.Topic
Message-Driven
• Nous devons configurer le produit qui implémente le
mécanisme JMS. Ici, il s’agit de JBoss MQ qui est intégré à
notre version de JBoss (4.0.5-GA)
• Le fichier de configuration doit être déposé dans le répertoire
<jboss-install>/server/<configuration-
name>/deploy/jms, et son nom doit se terminer par –
service.xml
Message-Driven
EJB 3
Message-Driven
EJB 3
Message-Driven
EJB 3
Message-Driven
• Il y a la possibilité de mettre un filtre sur les messages qui
peuvent être traités par un MDB. Cela permet de ne pas
recevoir certains messages. Cette propriété est surtout
intéressante dans le cas d’une Queue (PTP), car le message
n’est envoyé qu’à un seul MDB. Dans le cas d’un Topic (pub-
sub), cela permet surtout de ne pas avoir à tester si le message
reçu peut être traité par le MDB
• Pour d’avantage de précisions sur la propriété
messageSelector, nous vous renvoyons à la page 131 du
livre « EJB 3 in Action », 2007, Manning
Injection et JNDI
• L’annotation @Resource permet d’injecter un objet référencé (au sens large:
DataSource, JMS, EJB, Context, etc.). Il est possible de spécifier le nom JNDI de
l’objet pour lever une ambiguïté. Par exemple, dans le cas d’une DataSource, s’il y
en a plusieurs définies sur le serveur, il faudra alors donner son nom JNDI
(@Resource(name="jdbc/myDB")avec les EJB 2, dans le descripteur de
déploiement, on utilisait la balise <res-ref-name>)
• Il est également possible d’injecter des variables d’environnement (de type
String, Character, Byte, Short, Integer, Long, Boolean, Double
ou Float) qui ont été préalablement déclarées dans le descripteur de
déploiement :

• Lors du déploiement, le conteneur résout les références JNDI et relie les


ressources en question. Si une ressource n’est pas trouvée lors du déploiement, le
conteneur lance une RuntimeException; l’EJB est alors inutilisable
Intercepteurs
• Lorsqu’une méthode métier est appelée, les intercepteurs
permettent d’exécuter des méthodes avant que la méthode métier
ne soit exécutée
• Les intercepteurs peuvent être utiles dans plusieurs situations: pour
mettre des logs sans avoir à surcharger le code, pour gérer la
sécurité séparément du code métier, etc.
• Exemple pour vérifier le rôle de l’appelant :
Intercepteurs
EJB 3

• Les intercepteurs peuvent être défini au niveau du descripteur de


déploiement, de la classe, de la méthode (ordre dans lequel ils sont
appelés)
Callbacks
• Les callbacks permettent d’exécuter des méthodes lorsque
certains événements liés au cycle de vie de l’EJB interviennent
(création, suppression, hibernation, réveil)
• Après la création (@PostConstruct) (Stateless, Stateful,
Message-Driven)
• Avant la suppression (@PreDestroy) (Stateless, Stateful,
Message-Driven)
• Avant l’hibernation (@PrePassivate) (Stateful)
• Après le réveil (@PostActivate) (Stateful)
Intercepteurs et callbacks
• Session -> Stateless
Intercepteurs et callbacks
• Session -> Stateful
Intercepteurs et callbacks
• Entity
Intercepteurs et callbacks
• Message-Driven
Transactions
ACID
• Atomicité
Pour atteindre un but donné, une transaction est composée de plusieurs opérations qui
sont indispensables => soit toutes les opérations sont effectuées, soit aucune !

• Cohérence
Après qu’une transaction se soit réalisée, le base de données ou le système doit se trouver
dans un état cohérent

• Isolation
Une transaction doit se réaliser sans dépendre des autres transactions qui peuvent avoir
lieu en même temps

• Durabilité
Lorsqu’une transaction s’est terminée, le résultat de cette dernière est durable : que la
transaction ait été annulée ou qu’elle se soit terminée correctement, les opérations qu’elle
a effectué ne doivent pas disparaître

• Exemple : transfert bancaire


Transactions
• JTA (Java Transaction API) fournit un API permettant de gérer
les transactions :
– BMT (Bean-Managed Transactions) =>
@TransactionManagement(TransactionManagementTyp
e.BEAN)
– CMT (Container-Managed Transactions) =>
@TransactionManagement(TransactionManagementTyp
e.CONTAINER)
– Par défaut, le conteneur gère les transactions
• JPA (Java Persistance API) ne dépend pas du mode de gestion
des transactions => n’influe que sur les EJB Session et les
Message-Driven, et non sur les Entity
CMT
• Le conteneur démarre, valide ou annule la transaction. La
transaction commence au début de l’exécution de la méthode
métier appelée et se termine à la fin de cette dernière
Transaction
@TransactionAttribute existante lors Résultat
de l'appel ?

REQUIRED Non Le conteneur crée une nouvelle transaction


(par défaut) Oui La méthode rejoint la transaction existante
REQUIRED_NEW Non Le conteneur crée une nouvelle transaction
Oui Le conteneur crée une nouvelle transaction et la transaction
existante est suspendue
SUPPORTS Non Aucune transaction n'est utilisée
Oui La méthode rejoint la transaction existante
MANDATORY Non Lève une
javax.ejb.EJBTransactionRequiredException
Oui La méthode rejoint la transaction existante
NOT_SUPPORTED Non Aucune transaction n'est utilisée
Oui La transaction existante est suspendue et la méthode est
appelée sans transaction
NEVER Non Aucune transaction n'est utilisée
Oui Lève une javax.ejb.EJBException
CMT
• On peut forcer le fait que la transaction soit annulée
(context.setRollBackOnly())
• Les méthodes setRollbackOnly() et
getRollbackOnly() ne peuvent être appelées que
depuis un EJB dont les transactions sont gérées par le
conteneur (CMT) et ayant comme attribut de transaction
REQUIRED, REQUIRED_NEW ou MANDATORY
Sinon une IllegalStateException est levée !
• Si le conteneur détecte une exception système (principalement
les RuntimeException) comme une
ArrayIndexOutOfBoundsException ou une
NullPointerException, la transaction sera
automatiquement annulée
CMT
• Avec CMT, nous n’avons pas le contrôle sur le moment où la
transaction est démarrée, validée ou annulée
• Il est possible d’être informé de ces évènements grâce à des
callbacks
• L’EJB doit implémenter l’interface
javax.ejb.SessionSynchronization
– void afterBegin() — Est appelé juste après que le conteneur ait créé
une nouvelle transaction et avant que la méthode métier soit appelée
– void beforeCompletion() — Est appelé lorsque la méthode métier
s’est terminée et juste avant que le conteneur termine la transaction
– void afterCompletion(boolean committed) — Est appelé après
que la transaction soit terminée. Le flag committed indique si la transaction
a été validée (true) ou si elle a été annulée (false)
BMT
• On utilise la UserTransaction
– @Resource private UserTransaction userTransaction;
OU
– @Resource private SessionContext context;
...
UserTransaction userTransaction =
context.getUserTransaction();
• userTransaction.begin();
...
userTransaction.commit();
OU
userTransaction.rollback();
• La UserTransaction ne peut être utilisée que dans le cas où les
transactions sont gérées par l’EJB (BMT); dans le cas où c’est le
conteneur (CMT) une IllegalStateException est levée
Transactions distribuées
XA (eXtended Architecture)
• Afin de garantir les propriétés ACID vues précédemment, dans
un contexte où les sources de données sont distribués, il faut
que les drivers des bases de données soient de type XA
• L’architecture XA définit un protocole de validation en 2 phases
et un API pour la communication entre un transaction
manager et un resource manager
• Dans le cas d’une transaction où l’on doit enregistrer des
données dans 2 bases de données, que faire si lors de la
validation une se déroule avec succès et l’autre non ?
L’application se trouvera alors dans un état inconsistant et les
propriétés ACID ne seront plus respectées !
=> utilisation du protocole de validation en 2 phases
Sécurité
• JAAS (Java Authentication and Authorization Service) est une API standard
de Java permettant de gérer les identifications et les droits associés (par
rôles) au niveau du client et du serveur d'application
• Principe de fonctionnement :
– Dans un premier temps, JAAS authentifie (valide l'identité du client)
– Puis gère les autorisations (valide les droits d'accès pour un client authentifié)
– Les identités sont regroupées en rôles et les autorisations accordées par rôle
• Côté client, la technique la plus simple, mais aussi la plus ancienne et la
plus limitée : le username (Context.SECURITY_PRINCIPAL) et le
password (Context.SECURITY_CREDENTIAL) sont transmis avant le
lookup au contexte JNDI
Sécurité
• L’authentification est réalisée par une application web qui
ensuite appelle l’EJB en lui passant le Principal

• Pour sécuriser une méthode, il existe 2 possibilités :


– Avec les annotations (@RolesAllowed(...), @PermitAll,
@DenyAll)
– Avec la méthode isCallerInRole(...)
Sécurité
• Dans un premier temps, il faut configurer la sécurité JAAS sur le serveur
• Pour nous simplifier la tâche, nous utiliserons la méthode d’authentification
SimpleServerLoginModule :
– Si le mot de passe est null, alors l’utilisateur est authentifié avec le rôle guest
– Si le nom d’utilisateur est égal au mot de passe, alors l’utilisateur est authentifié avec les
rôles guest et user
– Sinon l’authentification échoue
• Pour cela, nous devons définir une politique de sécurité dans le fichier <jboss-
install>/server/<configuration-name>/conf/login-
config.xml
Sécurité
• Remarque : dans le cadre d’une vraie application, nous aurions
plutôt utilisé la méthode d’authentification
DatabaseServerLoginModule qui effectue une requête
dans une base de données qui contiendrait les noms
d’utilisateurs, les mots de passe, ainsi que les rôles associés
aux utilisateurs
• Dans les 2 exemples qui suivent, on déclare le domaine de
sécurité par l’annotation @SecurityDomain(...)
ATTENTION utiliser l’interface
org.jboss.annotation.security.SecurityDoma
in et non
org.jboss.aspects.security.SecurityDomain
Sécurité
• Avec les annotations
Sécurité
• Avec la méthode isCallerInRole(...)
Sécurité
• Pour pouvoir accéder aux méthodes précédemment
sécurisées, il faut que l’appelant soit correctement identifié
avec JAAS
• Prenons le cas d’une application web
Sécurité
• Définir le domaine de sécurité : fichier <your-web-
app>/WEB-INF/jboss-web.xml

• Définir ce qui sera sécurisé, comment, et quelle sera la page


d’authentification et la forme de celle-ci : fichier <your-
web-app>/WEF-INF/web.xml (contenu sur le slide
suivant)
Sécurité
• Page JSP qui permet d’appeler l’authentification JAAS : fichier
<your-web-app>/WEB-INF/jsp/login.jsp
Sécurité
• Appel de l’EJB depuis la Servlet :
Persistance avec JPA
• Intérêt de JPA: permet d’être indépendant du framework
gérant la persistance => si Hibernate venait à disparaître,
l’utilisation de son successeur ne demanderait que peu
d’adaptation
• Pour cette partie, nous partons du principe que vous êtes déjà
familiarisé avec Hibernate et avec le concept d’ORM (Object-
Relational Mapping)
• Au passage, nous profitons pour vous indiquer une excellente
référence : Hibernate 3.0, 2005, Eyrolles
Persistance avec Hibernate
• Un petit rappel sera tout de même le bienvenu !

• Les différents types de relations :


– one-to-one => <one-to-one name="user" class="User" constrained="true" />
(Email.hbm.xml)
– many-to-one => <many-to-one column="NATIONALITY" name="nationality“ class="Country" />
(User.hbm.xml)
– one-to-many => <set name="users" inverse="true">
<key column="NATIONALITY" />
<one-to-many class="User" />
</set> (Country.hbm.xml)
– many-to-many => <set name="bookmarks" table="WEBSITE_USER">
<key column="ID_USER" />
<many-to-many class="WebSite" column="ID_WEBSITE" />
</set> (User.hbm.xml)
Persistance avec JPA
• Activer la persistance JPA (META-INF\persistence.xml)

• Au préalable une DataSource


a été déclarée sur le serveur
(<jboss-install>/
server/
<configuration-name>
/deploy)
Persistance avec JPA
@Entity
• Cette annotation permet de spécifier qu’une classe doit être
persistée par JPA
• Cette classe doit posséder un constructeur public ou
protected sans argument

@Transient
• Permet de spécifier qu’il ne faut pas persister un attribut
donné
Persistance avec JPA
@Id
• Permet de définir quel champ sera utilisé comme clé primaire

@IdClass, @Embeddable + @EmbeddedId


• Permettent de définir des clés composées
• Nous n’allons pas les aborder dans le cadre de ce cours
• Nous vous renvoyons à la page 235 du livre « EJB 3 in action »,
Manning, 2007
ORM
• @OneToOne
Remarque: fonctionne très bien dans une utilisation « naïve », mais
cela devient problématique lorsque l’on veut utiliser des
fonctionnalités plus intéressantes, comme
@PrimaryKeyJoinColumn
[http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_S
equencing#Primary_Keys_through_OneToOne_Relationships]

• @OneToMany

• @ManyToOne

• @ManyToMany
ORM
ORM
ORM
ORM
• Inconvénients des annotations, surtout dans le cas de la
persistance JPA :
– Dégrade la lisibilité du code
– Perte de la séparation entre Entité et Mapping
– En cas de modification, nécessite de recompiler les classes modifiées,
au lieu de juste modifier les fichiers de mapping concernés
=> à utiliser plutôt dans le cas d’un prototype
• Solution plus élégante : utilisation d’un fichier de mapping XML
(META-INF\orm.xml) (contenu sur les slides suivant)
orm.xml (1/2)
orm.xml (2/2)
Héritage
• Avec JPA, il est également possible d’avoir une notion
d’héritage entre les entités
• Le développeur doit choisir la manière dont seront stockées les
informations (@Inheritance(strategy= ...)):
– une seule table (InheritanceType.SINGLE_TABLE)
– plusieurs tables liées (InheritanceType.JOINED)
– des tables indépendantes
(InheritanceType.TABLE_PER_CLASS)
• Pour d’avantage de précisions, nous vous renvoyons à la page
284 du livre « EJB 3 in Action », 2007, Manning +
http://en.wikibooks.org/wiki/Java_Persistence/Inheritance
Callbacks
• Comme pour les EJB session et les Message-Driven, il existe
des callbacks pour le Entity
• Avant / après la persistance d’une Entity (@PrePersist /
@PostPersist)
• Après avoir chargé une Entity lors d’une requête ou d’un
refresh (@PostLoad)
• Avant / après la mise à jour d’une Entity (@PreUpdate /
@PostUpdate)
• Avant / après la suppression d’une Entity (@PreRemove /
@PostRemove)
Références
• EJB 3 in action, 2007, Manning
• JBoss in action, 2009, Manning

• The J2EE 1.4 Tutorial, 2005, SUN


[http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html]
• EJB 2, 2005, SUPINFO [http://www.labo-sun.com/resource-fr-essentiels-
835-0-java-j2ee-ejb-2-les-entreprise-java-bean-javabeans-.htm]
• EJB 3, 2006, SUPINFO [http://www.labo-sun.com/resource-fr-essentiels-
836-0-java-j2ee-ejb-3-les-entreprise-java-bean-version-3-javabeans-.htm]
• Java Persistence, WIKIBOOKS
[http://en.wikibooks.org/wiki/Java_Persistence]
FIN

Merci pour votre attention !

You might also like