Published by

Il y a 11 ans -

Temps de lecture 17 minutes

Java EE 6, une plateforme simple et légère. Spring n’a qu’à bien se tenir !

La plateforme Java EE conserve de nos jours encore une mauvaise réputation. Les fameux EJB 2 et conteneurs lourds démarrant en plusieurs minutes vous rappelleront quelques bons souvenirs. L’arrivée de Spring a ouvert la voie aux conteneurs légers, à l’inversion de contrôle, ou encore à l’injection de dépendances; et est devenue la solution de référence. Cependant, la plateforme Java EE a beaucoup évolué entre temps.

Nous allons voir que Java EE 6 n’a maintenant rien à envier à Spring. Cette plateforme est devenue légère et simple à prendre en main. Toutes les spécifications ne seront pas abordées en détails.  Nous parlerons plutôt de conteneurs légers et testables, de managed beans, d’EJB Lite, ainsi que des nombreux services et patterns offert par la plateforme.  Nous terminerons par la spécification CDI et ses extensions portables, qui offrent de belles perspectives à la plateforme Java EE 6.

Spring, c’est quand même bien chouette!

Spring, Spring, Spring… Oui, Spring est un outil qui a révolutionné nos vies de développeurs et qui s’est très rapidement installé chez nos clients. Il a apporté avec lui des paradigmes de programmation comme l’IOC (Inversion Of Control), la DI (dependency injection), l’utilisation de simple POJOs, mais aussi de la simplicité avec ses xTemplate bien utiles. Arrivé en 2003, Spring s’est imposé rapidement dans les entreprises face au standard J2EE 1.4. Ce dernier était un peu Goliath J2EE. Souvenez vous de cette grosse usine à gaz avec ses EJB 2, son conteneur lourd packagé en ear, qui mettait des minutes à démarrer sur un serveur WAS.  Face à lui, un David nommé Spring doté de simples beans POJO, d’un conteneur léger et d’un packaging war déployable sur un simple serveur web, comme Tomcat. Grâce à eux,  Spring s’est imposé dans les entreprises en quelques années .

Java EE 5: début de la cure d’amaigrissement

Mais tout cela, c’était il y a neuf ans et depuis un sacré chemin a été parcouru par la spécification Java EE. En 2006, Java EE 5 est arrivé enrichi de nouvelles transformations. Tout d’abord, son utilisation a été rendue plus simple grâce à l’utilisation massive des annotations. Les EJB 3.0 en ont bénéficié et sont devenus ainsi injectables et plus faciles à implémenter. Il y a eu aussi l’arrivée de JPA, une nouvelle API de persistance, fortement inspirée d’Hibernate qui a fait disparaître les anciens Entity EJB CMP/BMP.

Java EE 6, ça rocks !

Sortie en 2009, Java EE 6 a progressé sur la voie de la simplicité avec l’utilisation massive des annotations et des POJO. A tel point qu’on peut se demander maintenant qui est David et qui est Goliath.
Des API existantes ont encore prospéré de manière significative, comme les EJB 3.1 (dont les EJB Lite), JPA 2.0, JSF 2.0, ou encore Servlet 3.0. De nouvelles API ont fait leur apparition comme CDI 1.0, @Inject 1.0, Interceptors 1.1, JAX-RS 1.1, ou encore Bean Validation 1.0. Et, toujours dans un esprit de “light is right”, de vieilles spécifications comme JAX-RPC, JAX-R, ou JSR88 ont été déclarées “pruned” et devraient disparaître pour la prochaine version de Java EE 7.
Par ailleurs, cette version introduit enfin des noms JNDI standard et donc portables entre les différents serveurs d’application. Cette version embrasse aussi les paradigmes de convention-over-configuration et de configuration-by-exception, qui permettent de ne presque plus rien avoir à configurer, le conteneur appliquant des paramètres par défaut tant qu’il n’ y a pas d’exception. Ainsi, on obtient des applications avec très peu de fichiers de configuration xml.
Avec Servlet 3.0, le bon vieux web.xml est devenu optionnel. Ainsi, ne soyez pas étonné de ne voir qu’en tout et pour tout seulement deux fichiers de configuration dans un projet Java EE: un fichier persistence.xml pour gérer la persistance avec JPA et un fichier beans.xml (qui peut rester vide) pour les managed beans.

Un packaging war grâce au Web Profile 1.0 et aux EJB Lite

L’arrivée des Profiles est une autre nouveauté majeure, ouvrant de nouvelles perspectives. En particulier, le Web Profile qui reprend un sous-ensemble de Java EE et qui permet de packager son application dans un simple war. Le Web Profile embarque les spécifications essentielles communément requises pour développer une application web d’aujourd’hui : JSF, JSP, Servlet, EL, JSTL, EJB Lite, CDI, etc. Cependant, il a été nécessaire de créer des EJB “Lite”, qui n’implémentent pas toute la spécification EJB. Ils conservent néanmoins le principal, à savoir : les Session Beans, l’injection, les interceptors, la transaction et sécurité. Ces sont donc de simples classes POJO, qui n’implémentent pas d’interfaces (appelée ‘no-interface view’). Les EJB Lite sont ainsi déployables dans un simple war.

//Exemple d'un EJB Lite avec une no-interface view
  @Stateless
   public class HelloBean {
       public String sayHello() {
           String message = propertiesBean.getProperty("hello.message");
           return message;
       }
   }

Des conteneurs légers et embarqués

Vous vous souvenez sans doute des serveurs d’application « usine à gaz », qui prenaient parfois quelques minutes à démarrer et à déployer votre application Java EE, . Il était aussi très difficile de tester ces EJB et de réaliser des tests d’intégration automatisés. Ces temps là sont maintenant révolus! Aujourd’hui, nous écrivons de plus en plus des tests unitaires et d’intégration. Nous avons besoin de déployer nos applications le plus rapidement possible pour ne pas être trop pénalisé durant notre cycle de développement. Pour répondre à ces besoins, les EJB 3.1 ont introduit l’idée de conteneurs JEE embarqués. Ces conteneurs EJB implémentent l’API javax.ejb.embeddable et sont packagés dans un jar exécutable n’importe où : du code Java SE, des tests Junit, ou encore un conteneur web. Ce conteneur s’exécute donc au sein de la même JVM que votre programme. Vous pouvez ainsi débugger facilement vos applications avec votre IDE préféré.

…et testables!

Voici un exemple de code Junit, où l’on peut voir comme il est simple de créer un conteneur EJB embarqué à l’aide de API javax.ejb.embeddable.EJBContainer. Des propriétés sont passées à l’ EJBContainer pour préciser où sont les ressources et classes Java. Ensuite, pour récupérer l’EJB, il suffit de faire un lookup dans le contexte en utilisant le nom JNDI portable de l’EJB.

private static EJBContainer ec;
private static Context ctx;

@BeforeClass
public static void createEmbeddedTestContainer() throws Exception {
       Map properties = new HashMap();
       properties.put(EJBContainer.MODULES, new File[]{new File("target/classes"), new File("target/test-classes")});
       ec = EJBContainer.createEJBContainer(properties);
       ctx = ec.getContext();
}

//Pour récuperer un EJB:
ctx.lookup("java:global/classes/MyEJB")

Par ailleurs, il existe une manière plus évoluée pour tester son conteneur EJB à l’aide de JBoss Arquillian et ShrinkWrap. Arquillian se pluggue sous forme de runner Junit et s’occupe de créer le conteneur pour vous. Il permet ainsi d’injecter directement avec @Inject vos beans. ShrinkWrap est un petit utilitaire qui permet quant à lui de créer une archive jar contenant les classes et fichiers ressources du projet. Arquillian à l’aide de l’annotation @Deployment va récupérer cette archive au moment de démarrer le conteneur. 

@RunWith(Arquillian.class)
public class MyEJBTest {

   @Deployment
   public static JavaArchive createDeployment() {
      return ShrinkWrap.create(JavaArchive.class, "test.jar")
         .addClasses(MyEJB.class, AnotherClass.class);
   }

   @Inject
   private MyEJB myEJB;

Les serveurs d’applications ont mis le turbo

Il existe quelques serveurs qui implémentent le Full Profile Java EE 6 et certifiés par Oracle, comme Oracle GlassFish Server 3.x, IBM WebSphere Application Server 8.0, ou encore Apache Geronimo 3.x. Mais grâce au Web Profile, on voit apparaitre de nombreux serveurs Java EE 6 Web Profile, comme: Jboss 7, Apache TomEE, ou encore Caucho Resin.

Prenons par exemple, Apache TomEE, qui n’est autre qu’un serveur Tomcat, auquel on a ajouté quelques jars, comme OpenWebBeans, OpenEJB, OpenJPA et MyFaces pour qu’il soit compatible Java EE 6 Web Profile. Comme le souligne Antonio Goncalves, Java EE expert member, ces serveurs démarrent très rapidement:  La plupart en moins de 4 secondes à comparer avec un simple Tomcat qui démarre en 0,7s. On oubliera WebSphere qui reste encore d’une lenteur accablante avec un démarrage en 48s. La version Full Profile de GlassFish nous rassure avec un démarrage en à peine 3s. On voit bien que Java EE devient de plus en plus rapide, simple à utiliser et s’oriente vers une architecture de plus en plus modulaire. 

Avec CDI, on injecte tout dans tout!

En parallèle de la notion de conteneur léger, l’API Managed Beans 1.0 décrit que toute classe POJO est potentiellement un bean managé par le conteneur. Ce dernier doit lui fournir un certain nombre de services simples, comme la gestion du cycle de vie du bean avec @PostConstruct et @PreDestroy, des interceptors “à la AOP” avec le @Interceptor, et le @AroundInvoke, ou encore de l’injection de dépendances avec @Inject, et @Resource.

CDI 1.0 est une API majeure dans Java EE 6 qui permet entre autre de réaliser l’injection de dépendance et bien plus comme nous le verrons par la suite. L’implémentation de référence est JBoss Weld. Ici, le mot d’ordre est loose coupling, strong typing. Contrairement aux beans Spring, qui sont identifiés par des String, et donc sources d’erreurs au runtime, CDI a choisi la voix du typage fort en utilisant des annotations. Tout managed bean est injectable dans un autre. Bref, maintenant on peut vraiment injecter tout dans tout ! A son chargement le conteneur CDI va scanner tous les jars de son classpath à la recherche d’un fichier beans.xml. Si un fichier est trouvé, il va créer des managed beans pour toutes les classes concernées. Ce type de chargement permet de réduire le temps de création du conteneur CDI et d’exclure facilement les jars que l’on ne veut pas manager. Par défaut, le fichier beans.xml peut rester vide, ce sont alors les annotations, qui seront utilisées. Si vous configurez le fichier, vous pourrez définir ou surcharger les propriétés d’un bean : les informations du fichier xml ayant prédominance par rapport aux annotations.

Les scopes

Tout bean dispose d’un scope. Par défaut, le scope est implicitement annoté @Dependent, ce qui revient à créer une instance du bean pour chaque objet dépendant de lui. D’autres scopes sont proposés: @RequestScoped, @SessionScoped, @ApplicationScoped, ou encore @ConversationScoped. Il est possible de créer ses propres scopes métiers. Il existe également une annotation @New pour forcer l’injection d’une nouvelle instance d’un bean, même si celui-ci a un scope Application par exemple.

@Inject @New Calculator calculator;

@ApplicationScoped
public class Calculator {...}

L’injection avec @Inject

Ainsi, pour injecter un bean, il suffit d’annoter une classe, une propriété, ou un paramètre avec l’annotation @Inject. Les beans sont identifiés de plusieurs manières possibles. La première méthode utilise un @Qualifier. Par défaut, si l’on ne précise rien, c’est le qualifier @Default qui est appliqué.

@Inject
private CustomerDao customerDao;

//Equivalent à:

@Inject @Default
private CustomerDao customerDao;

Identifier un bean avec @Qualifier

Si votre bean a plusieurs implémentations et afin de prévenir des ambiguïtés au niveau d’un point d’injection, vous devrez créer votre propre qualifier. Pour cela, il suffit de créer votre propre annotation avec @Qualifier et d’annoter votre bean avec celle-ci.

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface JdbcDao {
}

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface JPADao {
}

@JdbcDao
public class JdbcCustomerDao implements CustomerDao {...}

@JPADao
public class JPACustomerDao implements CustomerDao {...}


//Exemple d'injection avec @Qualifier:

@Inject @JdbcDao
private CustomerDao customerDao;

Identifier un bean via son nom EL avec @Named

Une autre manière d’identifier un bean est d’indiquer son nom EL (Expression Language) via l’annotation @Named. Ce bean sera ainsi accessible depuis vos pages JSF en utilisant une EL. Il est possible de spécifier un nom ou par défaut c’est le nom de classe dont la première lettre passe en minuscule qui est considéré.

@Named
public class Calculator {...}

//Equivalent à :

@Named("calculator")
public class Calculator {...}

//Page JSF:

<h:outputLabel value="#{calculator.someMethod}" />

Définir une implémentation alternative avec @Alternative

Une autre fonctionnalité intéressante proposée dans CDI est de définir une implémentation alternative à un bean. Cette alternative doit être déclarée dans le fichier beans.xml et permet de modifier l’implémentation d’un bean au déploiement. Elle surcharge donc le bean qui aurait dû être injecté initialement.

//MockCustomerDao est une implémentation alternative aux implémentations Jdbc et JPA
@Alternative
@JdbcDao
@JPADao
public class MockCustomerDao implements CustomerDao {...}

//L'implémentation alternative MockCustomerDao sera injectée à la place de JPADao:
@Inject @JPADao
private CustomerDao customerDao;

//Déclaration de l'alternative dans le beans.xml:
<alternatives>
   <class>fr.xebia.cdi.MockCustomerDao</class>
</alternatives>

Personnaliser le rôle d’un bean avec @Stereotype

Les stéréotypes permettent de regrouper au sein d’une même annotation des modèles et des rôles communs que l’on veut implanter sur des beans. CDI fournit quelques stéréotypes de base. Ci-dessous, le stéréotype @Model représente la partie Model du pattern MVC et peut être utilisée pour les beans JSF. On voit par exemple que les beans utilisant ce stéréotype auront tous en commun un nom EL, et un scope request.

@Named
@RequestScoped
@Stereotype
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface Model {}

On constate la richesse qu’offre CDI en plus de la simple injection de dépendances. Les qualifiers et stéréotypes permettent notamment de donner du sens métier et fonctionnel aux beans.

CDI offre de nombreux patterns clé en main

Outre l’injection de dépendance, CDI va plus loin en implémentant un certain nombre de patterns de programmation. Là encore, c’est à l’aide d’annotations que la magie opère.

Le pattern Singleton avec @Singleton

Tout d’abord le mythique pattern Singleton, où il suffit d’annoter un bean avec @Singleton. Ainsi, une seule instance du bean est partagée et les accès concurrents sont gérés. Il faut savoir que cette annotation est en fait un pseudo-scope, mais contrairement aux autres scopes, le conteneur injecte une référence directe au bean, et non un proxy.

Le pattern Factory avec @Produces

Cette technique appelée producer method est une méthode permettant de créer des objets injectables. On peut produire des types primitifs, des beans ou encore des ressources telles qu’un entityManager, une connection JMS, etc. Dans l’exemple ci-dessous, notre producer method produit un logger. Pour créer ce logger, il est nécessaire d’indiquer le nom de la classe qui sera loggué. Pour cela, il suffit de passer en paramètre de notre méthode un objet InjectionPoint qui sera injecté par CDI. Cet objet permet de récupérer des informations de contexte, comme la classe où est injecté l’objet Logger.

class LogFactory {

   @Produces Logger createLogger(InjectionPoint injectionPoint) {
       return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
   }
}

//Injection du logger:
@Inject
private Logger logger;

Les interceptors avec CDI

CDI enrichit l’API Interceptors 1.1 présente dans Java EE 6. Initialement, Interceptors permet de faire de l’interception à la « AOP » sur les managed beans ou les EJB beans. L’intercepteur déclenche alors un traitement avant et après l’invocation d’une méthode. L’annotation @AroundInvoke permet d’exécuter du code au moment de l’interception et @Interceptors indique les méthodes à intercepter. Ainsi, CDI introduit une nouvelle annotation @InterceptorBinding, qui simplifie et offre un couplage lâche avec la classe de l’interceptor.

@InterceptorBinding
@Inherited
@Target( { TYPE, METHOD })
@Retention(RUNTIME)
public @interface Transactional {}

//Intercepteur qui déclare l'annotation précédente, ainsi que @Interceptor
public @Transactional @Interceptor
class TransactionInterceptor { ... }

//On applique l'intercepteur sur le bean via l'annotation.
public @SessionScoped @Transactional
class ShoppingCart implements Serializable { ... }

Le pattern Decorator avec @Decorator

Les décorateurs ressemblent aux intercepteurs. Ils interceptent aussi des appels de méthodes, mais ont en plus accès à la sémantique du bean intercepté. Un décorateur est une classe abstraite (ou non) qui implémente le type du bean qu’il décore et est annoté avec @Decorator.
Le bean du décorateur s’injecte l’objet qu’il décore via les annotations @Inject @Delegate @Any. L’annotation @Delegate indique que ce point d’injection correspond au bean que l’on veut décorer. L’annotation @Any permet d’intercepter tous les beans implémentant l’interface du type décoré. Enfin, le décorateur implémente au choix les méthodes qu’il veut intercepter.

public interface Customer {
   String getName();
   ...
}

@Decorator
public abstract class CustomerDecorator
implements Customer {

   @Inject @Delegate @Any
   Customer customer;

   public String getName() {
      return "decorate"+ customer.getName();
   }

}

Le pattern Observer (ou Event) avec @Observable

CDI offre la possibilité aux beans de produire et de consommer des events. Ce mécanisme permet notamment aux beans d’interagir entre eux via un couplage lâche et asynchrone sans utiliser JMS. Par ailleurs, ce pattern permet de synchroniser et capter les changements d’état de beans stateful. 
Un event est matérialisé par un objet java (event object ou payload) et est marqué d’un ou plusieurs qualifiers (event qualifiers). Les qualifiers permettent de distinguer les events que l’on veut produire ou observer pour un type de bean donné. Techniquement, une méthode observatrice (observer method) aura le rôle de consommer l’event object, correspondant à un ensemble de qualifiers donnés.

//Permet de qualifier l'event
@Qualifier
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Updated {}

//Observer method pour tout type de Customer
public void onAnyCustomerEvent(@Observes Customer customer) { ... }

//Observer method pour les Customer qualifié par @Updated
public void afterCustomerUpdate(@Observes @Updated Customer customer) { ... }

//Producteur d'un event de type Customer qualifié par @Updated
@Inject @Updated Event<Customer> customerEvent;
public void someMethod(Event<Customer> customerEvent) {
   ...
   customerEvent.fire(customer);
}

Les extensions CDI, l’avenir de Java EE ?

De base, les patterns et les fonctionnalités qu’offre CDI sont riches et nombreux. Mais, cela va encore plus loin. Le cœur de CDI a été conçu tel un bus d’événements. Au moment du chargement du conteneur, celui-ci envoie ses informations de contexte (détection d’annotations, de beans, etc.) dans un bus. Il est alors possible de capter ces événements via une SPI, contenu dans le package javax.enterprise.inject.spi. En implémentant cette SPI, il est ainsi possible de capter ces informations et d’effectuer des traitements propres. Cette architecture rend CDI modulaire et permet le développement d’extensions CDI portables.

Les développeurs de Seam 3 ont bien compris l’intérêt des extensions CDI et ont conçu leur framework comme un ensemble d’extensions à CDI. Si vous avez besoin d’une certaine fonctionnalité, il suffit d’ajouter le module concerné. Cela ouvre la voix à l’intégration d’api third-party et permet d’enrichir considérablement la plateforme Java EE,  de la même manière que Spring le fait avec ses modules et ses templates.

Par ailleurs, un projet très ambitieux encore en incubation chez Apache, nommé DeltaSpike a pour but de standardiser un ensemble d’extensions CDI, développé et maintenu par la communauté Java. Et éventuellement, ces extensions pourront être intégrées aux spécifications Java SE et EE. Les premiers contributeurs ont offert les « code base » de JBoss Seam3, Apache MyFaces CODI et CDISource. A terme, JBoss Seam 3 et consorts disparaitront pour laisser DeltaSpike la seule source d’extensions CDI portables et standards. L’avenir de Java EE passera donc aussi par le développement des extensions CDI.

Conclusion

Cet article avait pour vocation de démontrer que la plateforme Java EE 6 est aboutie et apporte des nouveautés majeures. Il n’a pas abordé toutes les spécifications, telles que JPA 2, Bean Validation 1.1 ou encore JSF 2.0.  Comme nous avons pu le voir, cette plateforme a rattrapé son retard par rapport au géant Spring. L’arrivée du Web Profile et des EJB Lite ouvre la voie à des conteneurs légers et testables.

Néanmoins, il y a quelques petites déceptions: par exemple, la gestion de la configuration et celle des profiles sont inexistantes contrairement à spring. On peut aussi regretter que le profile web n’inclue pas JMS, JAX-RS et JAX-WS. Enfin, la réussite de Java EE 6 est sans doute l’API CDI, qui apporte enfin l’injection de dépendances, de la modularité, et même plus avec ses extensions. Le projet Delta Spike qui consiste à devenir le catalogue de référence des extensions CDI est à surveiller de près.

Published by

Commentaire

23 réponses pour " Java EE 6, une plateforme simple et légère. Spring n’a qu’à bien se tenir ! "

  1. Published by , Il y a 11 ans

    Je suis d’accord avec vous : la spec JEE a bien évolué ces dernières années et c’est pour le mieux.

    Néanmoins Spring reste une pile technologique intéressante.
    Notamment sur
    1/ la testabilité (@Inject => difficilement testable) : le mécanisme @Configuration est bien meilleur à mon avis car il permet d’avoir de vrai POJO et de centraliser la création du graphe objet (et de ne pas en avoir dans les tests).
    2/ la persistence : spring-data est en avance sur JEE
    3/ la galaxie de « third party API » compatible (ex: spring-batch, spring integration, spring-social, …)
    4/ sur la partie web, produire soit du xml soit du JSON avec la même méthode est plus simple avec Spring
    5/ la gestion native des évènements synchrones (i.e applicationEventSender.send(new MyEvent(…)) + onEvent(MyEvent e) )

    Bref, les 2 piles évoluent et à mon sens Spring a encore une petite avance et devrait la conserver, notamment parce que le processus de décision est bien plus rapide !

  2. Published by , Il y a 11 ans

    @Chris
    1/ Pour la testabilité il y a le framework Arquillian qui permet de tester tous les container, leur interactions… Pas besoin de mock ou de setter explicitement les dépendances pour les tests. Avec JEE les tests unitaires disparaissent, vive les tests d’intégration

    4/ Hmm… Avec JAX-RS (si on parle bien de service web REST) il suffit de renvoyer un POJO annoté @XmlRootElement et le framework va sérialiser en XML ou en JSON selon que la méthode exposée déclare @Produces(« application/json ») ou @Produces(« application/xml »). Je vois pas comment on peut faire plus simple.

    Sur les autres points je suis d’accord. Spring va plus vite mais c’est essentiel à sa « survie »

  3. Published by , Il y a 11 ans

    Autre remarque sur la production de Json/XML: si on veut que ce soit la même méthode qui produise les 2, on met les 2 dans @Produces et c’est le client qui décide ce qu’il veut avec l’entête HTTP Accept

  4. Published by , Il y a 11 ans

    je ne savais pas qu’on pouvait avoir 2 @Produces + accept header (c’est ce qui est fait dans spring aussi mais pas besoin de répéter @Produces sur toutes les méthodes) => spring perd cet avantage

    concernant la disparition des tests unitaires, c’est bien le reproche que je fais.

    par exemple, tu as une classe avec un collaborateur qui doit avoir un comportement différent durant les tests (ex : tu dos tester que tu envoies un mail sans envoyer de mail…). Comment tu fais dans JEE ?

    Dans Spring, au moment où tu crées tes POJOs dans le test, tu injectes soit un mock soit un fake:

    Mailer mailer = mock(Mailer.class)
    new MyClass(mailer).doIt();
    ensure(mailer, once()).send(); //verifi que mailer a été appelé une fois

    ou

    FakeMailer mailer = new FakeMailer();
    new MyClass(mailer).doIt();
    Assert.assertEquals(1, mailer.getSentMails().size());
    Assert.assertEquals(« toto@t.com », mailer.getSentMails().get(0).getTo());

  5. Published by , Il y a 11 ans

    Pour tester l’envoi de mail j’utilise Wiser (http://code.google.com/p/subethasmtp/wiki/Wiser). C’est fait pour les tests (c’est un Fake donc, quoique je crois qu’il puisse réellement envoyer des mails) et je peux configurer une ressource dans le serveur d’app embarqué et récupérer ma Session JavaMail via l’annotation @Resource.

    Docn pour ma part, j’ai un fichier de conf pour mon serveur d’app « normal » et un pour les tests.

  6. Published by , Il y a 11 ans

    Il existe plusieurs solutions pour tester unitairement un EJB (ou un bean):

    1- On mocke tout, donc pas besoin de déployer le conteneur JEE. Il suffit d’instancier soi-meme une instance de l’implémentation à tester et de lui injecter les mocks (créer avec Mockito par exemple).

    2 – On mocke une partie, mais pas tout. On déploie le conteneur JEE et injecte le bean testé via Jboss Arquillian, puis on injecte à la main les mocks via des setters du bean.

    3 – Variante du 2, où l’on mocke une partie, mais pas tout. Au lieu d’injecter soit même des mocks à la main, on peut laisser le conteneur injecter des mocks, qui sont en fait des implémentations @Alternative (déclarées dans un beans.xml pour les tests).

  7. Published by , Il y a 11 ans

    Superbe article qui me conforte dans l’idée de la nette progression de JavaEE6 comparé au framework Spring.
    Encore merci

  8. Published by , Il y a 11 ans

    Les temps de démarrage sont chronométrés comment ? Ce sont des temps de démarrage à vide ? (sans war ou ear ou un simple helloworld)
    Perso, j’ai jamais retrouvé ces temps de démarrage même avec Jboss AS 7 qui à vide démarre en moins de 2s mais dès qu’on a une vraie appli (plus qu’une pauvre servlet), il démarre plus lentement qu’un Tomcat :-P

  9. Published by , Il y a 11 ans

    @Nicolas: Merci, et je suis tout à fait d’accord avec toi. Ca donnerait presque envie de migrer l’application Spring vers Java EE 6 chez mon client :p

    @Kris: Ces temps ont été mesurés la plupart du temps avec la web application d’administration de chaque serveur.

  10. Published by , Il y a 11 ans

    @Romain JavaEE 6 j’y suis déjà et je regrette vraiment pas, c’est que du bonheur ;)

    @Kris tu trouveras sur le blog d’Antonio Goncalves son benchmark sur les différents serveurs d’application : http://agoncal.wordpress.com/2011/10/20/o-java-ee-6-application-servers-where-art-thou/ et ces prérequis :

    « To calculate the startup time, I don’t do any fancy rocket science either. I just start the server a few times, check how long it takes and use the best startup time. Also remember that some servers do not load any container at startup, making them very fast to start. That’s why I first trigger the web admin console (when there’s one) so I’m sure at least one web application is deployed (I did a print screen of the admin console when there’s one). Then I just stop and start the server a few times and get the best startup time (by reading the logs). To calculate the memory footprint, I just use the Windows task manager and check the size of the java.exe process. »

  11. Published by , Il y a 11 ans

    @Nicolas c’est ce que je pensais, il n’y a pas vraiment d’application de lancer a part la console d’admin :-S. Ce qui nous intéresse surtout c’est le temps de démarrage dans la vie réel, lorsqu’on développe une « vraie » application et qu’on doit redémarrer son serveur assez souvent. Il faudrait copié le mécanisme de Play pour la dev ^_^.

    Remarques, il serait intéressant aussi de voir si le chargement du container Spring et plus rapide ou plus lent que le container du serveur d’appli :-)

  12. Published by , Il y a 11 ans

    @Kris
    Là je peux te dire, je bosse sur une appli avec presque toute la stack JEE6 (JSF2, CDI, EJB, JAX-RS, JPA2 (Hibernate)):
    voila ce que glassfish 3.1.1 (full profile) vient de m’afficher:
    GlassFish Server Open Source Edition 3.1.1 (12) heure de démarrage : Felix (2 232ms), services de démarrage(33 831ms), total(36 063ms)

    Quand je redemarre, Felix ne redémarre pas (je pense). Cela dit j’ai pas une machine des + récentes (elle a 3 ans)

  13. Published by , Il y a 11 ans

    @Kris « c’est le temps de démarrage dans la vie réel, lorsqu’on développe une « vraie » application et qu’on doit redémarrer son serveur assez souvent »

    Pour ma part, avec spring, tomcat met 22s à démarrer pour une appli de 80000 lignes qui contient session factory avec 290 entities (je dirais à vue de nez que 50% du temps = liquibase qui vérifie qu’il n’a rien à faire, 30% = hibernate; 10% = openoffice, 10% = instantiation de spring, notamment scanning des @Configuration et @Contrller).

    Néanmoins, je redémarre peu souvent : je développe en TDD la logique métier qui est exposé via un ws rest json.
    Ensuite, le gui est entièrement en php+js => pas de rédémarrage.
    En moyenne, je dois redémarrer 8 fois par jour…

  14. Published by , Il y a 11 ans

    @Thibaud @chris c’est bien ce que je pensais, on est plus vers les 30s que des 3s :-P. Merci pour vos précisions :-)

  15. Published by , Il y a 11 ans

    @Chris

    Perso, avec Java EE, pour faire mes tests unitaires, je fais du mock (Mockito).
    Et je définis ces mocks uniquement en Java, sans Spring, car c’est toujours plus rapide de configurer mon mock en Java qu’en le définissant en XML avec Spring.

    Bref, je ne vois pas ce que Spring pourrait m’apporter de plus pour les tests unitaires. De fait, pour mon projet actuel, Spring n’est plus utilisé pour nos tests unitaires.

    Sinon, la section « Définir une implémentation alternative avec @Alternative » semble offrir une voie pour définir, avec CDI, une implémentation ad hoc pour les tests.

  16. Published by , Il y a 11 ans

    A la lecture de l’article, je déduis que CDI ~= DI + AOP-like + EventBus

    Est-ce que les extensions de CDI pourraient appartenir à une catégorie que ces 3 catégories là ?

    Merci

  17. Published by , Il y a 11 ans

    Je suis tombé sur un article très intéressant présenté durant JavaOne 2011:
    http://www.slideshare.net/ertmanb/javaone-2011-migrating-spring-applications-to-java-ee-6
    On voit notamment à la fin des exemples de code concret pour créer son extension CDI et migrer en douceur de Spring vers Java EE.

    @Dominique De Vito: En effet, CDI implémente en quelque sorte DI. DI n’est finalement qu’une spécification décrivant une sémantique (des annotations) pour l’injection de dépendances.
    Je crois qu’il manque un mot dans ta question, je ne suis pas sûr d’en comprendre son sens. En tout cas, les extensions CDI font partie intégrante de la spécification CDI via un SPI (service provider interface). Et, elles utilisent le « bus événementiels » de CDI.

  18. Published by , Il y a 11 ans

    @Dominique De Vito
    Tu oublies le « C » (Context) de CDI dans ton résumé. Le container CDI gère le cycle de vie des beans par rapport à des contextes web (Request, Conversation, Session, Application) – Le premier nom était « Web Beans ».

    Pour (essayer) de répondre à ta question, les extensions vont fournir des nouveaux composants (tout fait) au framework. Je prend des exemples de Seam3 (que j’ai déjà un peu manipulé): Cron Module, Social Module, Mail Module …

    Un peu comme ce qu’il y a dans le monde Spring… En gros CDI c’est peu pensé pour être le Spring-killer (impression personnelle)

    Enfin, pour ceux que CDI intéresse je vous conseille de lire la specs. Elle se lit très bien (comparée à d’autres) avec des exemples de codes. un vrai « user guide »

  19. Published by , Il y a 11 ans

    @Dominique de vito
    pas de Spring dans les tests unitaires, uniquement dans les tests d’intégration (etr encore, ceux de très haut niveau)
    Et en plus, dans ce cas, j’utilise @Configuration et pas de XML.
    Le XML, c’est uniquement pour la conf (ex: DB, JMS, …)

    IMHO ce qu’apporte @Configuration par rapport à @Inject, c’est que tu maitrise entièrement ton graphe d’objet et surtout, tu as une erreur de compilation quand tu ajoutes un argument à ton constructeur (et aussi, ton code n’est pas pollué d’annotation dans tous les sens…)

  20. Published by , Il y a 11 ans

    @Romain Schlick

    Oui, je corrige ma question initiale:
    Est-ce que les extensions de CDI pourraient appartenir à une catégorie autre que les 3 catégories (de fonctionnalités) suivantes pour CDI : DI + AOP-like + EventBus ?
    Merci

  21. Published by , Il y a 11 ans

    @Thibaud

    « Le container CDI gère le cycle de vie des beans par rapport à des contextes web (Request, Conversation, Session, Application) – Le premier nom était « Web Beans ». »

    oui, d’ailleurs, j’avais vu dans CDI ex-WebBeans un « kind of a Java application bus (somewhat similar to CORBA) », un genre de bus de composants ou d’events revisité à la sauce Java et qui permet de « coller » ou, plus simplement, de relier ces différents composant ensemble : http://www.jroller.com/dmdevito/entry/seam_is_a_kind_of

  22. Published by , Il y a 11 ans

    Le terme « bus » ça fait un peu peur…
    Le système d’évènements dans CDI est très simple:
    – Un objet Event qu’on @Inject avec une méthode fire(T monPojo)
    – Une annotation @Observes
    et c’est tout. C’est plus proche du pattern « notifier/observer »
    On se retrouve avec des beans qui n’ont plus aucune relations explicites (du point de vue compilation).
    L’exemple dans la specs page 6 tient sur une page.

    C’est même un peu déroutant et faut faire attention quand on refactorise/change le code. C’est là que les tests d’intégration sont utiles…

  23. Published by , Il y a 11 ans

    @Thibaud

    J’ai repris la métaphore du bus pour CDI ex-WebBeans, pas pour le sous-ensemble « notifier/observer » que vous citez.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.