I. Historique

Un premier jet des spécifications EJB 3.1 a été mis en ligne fin mars 2008 par le groupe d'experts de la JCP. Elles feront partie de la plate-forme Java EE 6.

Les EJBs ont 10 ans. Les dates correspondent aux spécifications (releases) finalisées.

  • EJB 1.0 : 21 mars 1998
  • EJB 1.1 : 12 décembre 1999
  • EJB 2.0 : 14 août 2001
  • EJB 2.1 : 12 novembre 2003
  • EJB 3.0 : 18 décembre 2005 (JSR 220, plate-forme Java EE 5)
  • EJB 3.1 : 21 février 2008, la JSR 318 non finalisée (disponible avec la plate-forme Java EE 6, prévu fin 2008)

Jusqu'à la version 2.1, les EJB bénéficiaient d'une mauvaise réputation à cause de leur complexité. Depuis la version 3.0, leur mise en place est grandement simplifiée: plus besoin de descripteur de déploiement, plus de Home interface, plus besoin de faire un lookup via JNDI pour récupérer des EJBs, il suffit de passer par l'annotation @EJB, etc ...

La version 3.1 va vers encore plus de simplicité et de portabilité. Il faut espérer que les vendeurs de serveurs d'application implémentent rapidement cette nouvelle spécification pour pouvoir l'utiliser dès que possible.

Cet article n'est pas exhaustif, les spécifications finales étant encore en cours d'écriture mais il permettra au lecteur d'avoir un aperçu rapide des nouveautés dans les EJB 3.1, par rapport à la version 3.

II. Plus besoin d'interfaces locales !

Il n'est plus nécessaire d'implémenter une interface locale pour écrire des sessions beans, ce qui était déjà le cas avec les JPA entités et les Message Driven Beans.

 
Sélectionnez
  1. @Stateless   
  2. public class MonStatelessBean {   
  3.    
  4.   public String pause(Temps temps) {   
  5.     return "Un thé";   
  6.   }   
  7. }  

On voit qu'il n'est plus nécessaire d'utiliser l'annotation @Local et d'implémenter une interface. Un simple POJO suffit, annoté avec  @Stateless  ou  @Stateful  .

III. Le Singleton Bean et la concurrence d'accès

Le Singleton Bean correspond bien évidemment au classique design pattern Singleton.
C'est un POJO avec des annotations.
Le but est d'obtenir un objet transactionnel, thread-safe et qui fournit tous les services d'un container EJB : concurrence, sécurité ...
On a ainsi une instance unique d'un stateful session bean qui est partagé par tous les clients et qui supporte les accès concurrents. 

Exemple d'un singleton avec des données en écriture :

 
Sélectionnez
  1.  @Singleton @ReadOnly   
  2. public class ContextBean implements ContextPartage  {   
  3.    
  4.   @PostConstruct   
  5.   public void init() {   
  6.     //on initialise le contexte de l'application   
  7.   }   
  8.     ...   
  9. } 

On peut gérer la concurrence d'accès à ce singleton avec les annotations @ReadOnly et @ReadWrite qui permettent de poser un verrou en lecture-écriture sur l'instance unique. 
La méthode init() sera lancée une seule fois, quand le container démarre l'application, à l'initialisation. 
Les singletons peuvent donc être injectés (design pattern Inversion of Control IoC). 

Exemple d'un singleton avec des données changeables :

 
Sélectionnez
  1. @Singleton    
  2. public class ContextBean implements ContextPartage {   
  3.    
  4. private ContextPartage contextePartage;   
  5.    
  6. @ReadWrite public void condition(...) {   
  7.     // mise à jour du contexte partage   
  8.     ...   
  9. }   
  10.    
  11.  @ReadOnly public int getValeur() {   
  12.     return contextePartage.valeur;   
  13. }   
  14.     ...   
  15. } 

La concurrence d'accès peut être gérée soit par le container, soit par le développeur du bean (@BeanManagedConcurrency). 

Note (mars 2010) : les annotations @ReadOnly et @ReadWrite sont remplacées par @Lock(LockType.READ) et @Lock(LockType.WRITE)

IV. EJB Timer

Une nouvelle annotation fait son apparition : @Schedule

 
Sélectionnez
  1. @Schedule(hour="10", dayOfMonth="1")   
  2.  public void uneTache() {   
  3.   ...   
  4.  }  

Cette tâche est lancée le 1er jour de chaque mois, à 10h00. 
Il est maintenant possible d'activer un scheduler similaire à la Crontab sous Unix.
Avant on devait passer par l'interface javax.ejb.TimerService :

 
Sélectionnez
  1. @Stateless   
  2. public class ControleTrafficBean implements ControleTraffic {   
  3.     @Resource TimerService timerService;   
  4.        
  5.     public void controleTrain(Train train) {   
  6.         ...   
  7.         // en millisecondes   
  8.         // createTimer(long initialDuration, long intervalDuration, Serializable info)    
  9.         timerService.createTimer(10*60*1000, 60*60*1000, train);   
  10.         ...   
  11.     }   
  12.        
  13.     @Timeout   
  14.     public void annulerTrain(Timer timer) {   
  15.       ...   
  16.         Train train = (Train) timer.getInfo();   
  17.       ...   
  18.     }   
  19. } 

Ici on crée un intervalle de 10 minutes, qui se répète toutes les heures.
La méthode annotée avec @Timeout est activée quand l'intervalle de 10mn est passé.
Avec la version 3.1, les choses deviennent encore plus simples, on écrit de manière déclarative :

 
Sélectionnez
  1. @Stateless   
  2. public class ControleTrafficBean implements ControleTraffic {   
  3.    
  4.     @Schedule(second="0", minute="10", hour="*",    dayOfMonth="*", month="*", year="*")   
  5.     public void controleTrain(Train train) {   
  6.                 ...   
  7.     }   
  8. }  

On peut donc automatiquement créer des timers.

V. Appels asynchrones

Avec les EJB 3.0, les appels asynchrones se font exclusivement via les Message Driven Beans et JMS.
Depuis les EJB 3.1, les méthodes des sessions beans peuvent aussi être appelées de façon asynchrone:
quand le client appelle une méthode asynchrone, le container redonne la main au client et continue l'appel de cette méthode asynchrone dans un autre thread. 
Pour cela, on utilise l'annotation @Asynchronous :

 
Sélectionnez
  1. @Stateless @Remote(DemandeFacade.class)   
  2. public class DemandeFacadeBean implements DemandeFacade{   
  3.    
  4.   @Asynchronous   
  5.   public void enregistre(Demande demande) {   
  6.     // ...   
  7.   }   
  8.    
  9.   @Asynchronous   
  10.   public Future autorise(Demande demande) {   
  11.     // ...   
  12.      return new AsyncResult(DemandeEtat.OUI);   
  13.   }   
  14. } 

Les 2 méthodes ci-dessus sont asynchrones.
Les types de retour possibles sont void ou java.util.concurrent.Future comme c'est le cas pour la méthode autorise(...) .
Cette interface fournit des méthodes qui permettent de vérifier le résultat d'un appel asynchrone, l'annuler ...
La méthode autorise(...) utilise la classe helper javax.ejb.AsyncResult qui possède un constructeur qui prend comme paramètre le résultat. Cette classe implémente Future.

VI. EJB dans un WAR

Pour faciliter le déploiement, il est maintenant possible de packager / déployer des composants EJB 3 dans une archive WAR.
Il n'est plus nécessaire de créer une archive JAR et une archive EAR, tout peut être contenu dans une unique archive WAR.
Les classes peuvent aller dans le répertoire WEB-INF/classes, ainsi que le descripteur de déploiement ejb-jar.xml s'il est utilisé,
ou bien le JAR des EJB peut aller directement sous WEB-INF/lib/ . 
On peut désormais appeler des EJB directement dans un container de servlet comme Tomcat, tout tient dans un .WAR.

VII. Support des stateful web services

Les EJB 3.0 supportent les stateless web services. Avec la version 3.1, les stateful web services seront aussi supportés.

 
Sélectionnez
  1. @WebService   
  2. @Stateful   
  3. public class BonjourBean {   
  4.        
  5.         public String direBonjour() {    
  6.           return "Bonjour";   
  7.         }      
  8. } 

Note (mars 2010) : cette nouveauté n'est finalement pas supportée.

VIII. Profils et EJB Lite

VIII-A. Profils

Java EE 6 introduira la notion de  profil  : les profils offriront différents types de services JEE.

  • Profil A : c'est une version légère (Servlet, JSP etc).
  • Profil B : il inclut le profile A + EJB 3.1 Lite, JTA, JPA, JSF et les WebBeans.
  • Profil C : c'est une version complète de la plate-forme constituée du profil B + JMS, JAX-WS etc.

VIII-B. EJB Lite

L'idée est de permettre une implémentation d'un sous-ensemble des EJB 3.1 en enlevant les choses les moins utilisées, comme les Message Driven Beans et les EJB Timer services. 

Caractéristiques EJB Lite EJB
Stateless beans oui oui
Stateful beans oui oui
Singleton beans oui oui
Message driven beans   non
Pas d'interfaces oui oui
Interfaces locales oui oui
Remote interfaces beans   oui
Interfaces Web Services   oui
Invocation asynchrone   oui
Interceptors oui oui
Sécurité déclarative oui oui
Transactions déclaratives oui oui
Transactions programmatiques oui oui
Timer service   oui
Support EJB 2.X   oui
Interopérabilité CORBA   oui

IX. Liens

X. Remerciements

Je tiens à remercier Hikage, Ricky81 pour les conseils, remarques et relectures, ainsi qu'Aspic pour la correction orthographique. 

Je remercie aussi www.developpez.com me permettant de publier cet article et Nono40 pour ses outils.