Software Craftmanship

JavaEE fait peau neuve, bienvenue à Jakarta EE

Découvrez nos jobs
Vous ambitionnez de devenir Tech Lead ou de faire du conseil de haut-niveau ? Nous avons des challenges à votre hauteur !

Le 10 septembre 2019, la fondation Eclipse publia sous licence opensource Jakarta EE 8.

Pourquoi ce nom Jakarta EE ?

Baaa, Jakarta c’est la capitale de l’Indonésie. Elle est située à l’extrémité nord-ouest de l’île de …

Je vous le donne en mille, Java.

Avant de rentrer dans les détails, revenons quelques temps en arrière

En 1999, la société Oracle décide de créer une extension à la plateforme standard (Java Standard Édition). Cette extension est destinée aux entreprises pour faciliter le développement notamment d’applications Web. Cette extension est appelée J2EE (Java 2 Entreprise Édition).

Huit versions ont été publiées en 18 ans. Le cycle de releases n’a pas été régulié et est devenu de plus en plus long.

Mais c’est quoi exactement le JavaEE ?

Une version JavaEE est composée d’un ensemble de JSR.

JSR est l’acronyme de Java Spécification Request, elle peut désigner la plateforme SE (Standard Edition) ou EE (Entreprise Edition).
Une JSR est composée d’une :

L’ensemble des JSR sont répertoriées sur le site de la Java Community Process (JCP).

Pour démystifier la chose, prenons la JSR-365 standardisant l’injection de dépendance.
Cette JSR appelée CDI possède pour sa version 2.0-final :

Il existe d’autres implémentations comme par exemple OpenWebBeans, qui pour être certifié a dû passer par la case TCK.

En pratique

Créez un projet java standard via maven et importez l’api de CDI dans l’un de vos projets :

<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>2.0</version>
</dependency>

Jetons un oeil sur ce que contient l’api:

Vous remarquerez qu’elle est uniquement composée d’Interfaces, Annotations et Exceptions. Tous ces objets sont mentionnés et décris dans les spécifications de la JSR.

Pour que votre projet puisse utiliser le système d’injection de dépendance, il faut lui fournir une implémentation:

<dependency>
  <groupId>org.jboss.weld.se</groupId>
  <artifactId>weld-se-core</artifactId>
  <version>3.0.0.Final</version>
</dependency>

Ce système de spécification / implémentation se base sur un mécanisme créé pour la version 6 de java, les Services Provider Interface (ce sujet fera l’objet d’un article à part entière).

Nous l’avons vu plus haut, JavaEE ce n’est pas seulement une api d’injection.
Voici la cartographie des JSRs présentes dans la spec JavaEE 8:

Ça en fait un tas de services prêts à utiliser !

Lorsque vous téléchargez un Serveur JavaEE, vous récupérez la totalité des 33 JSR. Prenons l’exemple de la première version de Wildfly implémentant JavaEE 8, elle pèse 162 Mo. Ca fait beaucoup !

JavaEE à partir de sa version 6 introduit la notion de profile. La version Full contiendra l’intégralité des JSR tandis que le profile Web sera uniquement composé des JSR blanches (14 JSR de moins, de quoi alléger le poids du serveur).

Quelques serveurs JavaEE

Vous les connaissez tous. Derrière ces serveurs se cachent les plus gros acteurs de la scène informatique, respectivement IBM, Red-Hat, Oracle.

Chaque serveur est libre de fournir l’implémentation de son choix des JSR JavaEE.

Prenons l’exemple de la JSR JPA:

ServeursGlassfishWebSphereJBossWildflyWebLogicSAP Cloud Platform
ImplémentationsEclipseLinkEclipseLinkHibernateHibernateEclipseLinkEclipseLink

Quels étaient les objectifs du JavaEE ?

Proposer des API standards utilisables par tous les serveurs JavaEE.

L’idée est qu’un code java fonctionnant sur un serveur, par exemple Glassfish, pourra s’exécuter sans modification sur un serveur Wildfly.

La grande force du JavaEE c’est que tout le monde le connait. Nous travaillons avec depuis des années et cela même peut-être sans le savoir. Un développeur travaillant sur une application Spring Boot aura sans doute eu l’occasion d’utiliser l’api JPA avec Hibernate.

Désamour pour les serveurs JavaEE

En avril 2014, Spring boot débarque et donne un bon coup de pied dans la fourmilière. Ses fonctionnalités d’import via un bom le rendent plus flexible, léger, rapide. Il est aussi plus facile à tester. Pour finir, son cycle de release est plus court que celui de JavaEE.

Fin d’année 2017, coup de tonnerre dans le monde du développement Java, la société Oracle détentrice des droits d’exploitations de la licence JavaEE décide d’arrêter sa maintenance !

Et Jakarta EE dans tout ça ?

Jakarta EE est la version open-source de JavaEE. Dès novembre 2017 la Fondation Eclipse créé le projet Eclipse Enterprise for Java (EE4J). Il lui faudra 2 ans pour définir une structure capable de faire évoluer le projet.

L’equivalent du JCP s’appelle dorénavant Eclipse Foundation Specification Process (EFSP). Les TCK existent toujours, en revanche les implémentations de références n’existent plus.

Une release sera publiée tous les ans !

Vous trouverez le détail du nouveau processus de développement dans ce lien.

Y a-t-il de nouveau dans Jakarta EE 8 ?

A vrai dire pas grand chose. L’essentiel des efforts a été mené sur la création d’une nouvelle structure, cependant quelques JSR ont été montées de version :

JSRJavaEE8=>JakartaEE8
Transaction1.21.3
Deployment1.21.7
Concurrency1.01.1
Activation1.11.2
SOAP with Attachments1.31.4

Y-a t-il des serveurs certifiés Jakarta EE 8 ?

Hé oui, et pas qu’un seul ! Eclipse GlassFish, Payara, Wildfly, Open Liberty

Comment migrer une application JavaEE 8 vers Jakarta EE 8 ?

Lecteur vidéo00:0000:31

Rien de plus simple, il vous suffit de changer votre pom ! Les packages sont identiques, aucune modification de code sera nécessaire !

TRÈVE DE BAVARDAGE, PARLONS CODE !

Voici une petite application de gestion d’animalerie spécialisée en l’aquariophilie.

PRENONS QUELQUES COMPOSANTS ET FAISONS RÉFÉRENCES À SES JSR RESPECTIVES

JPA / Beans Validation

@Entity
@NamedQueries({
        @NamedQuery(name = "Fish.getAll", query = "select f from Fish f"),
        @NamedQuery(name = "Fish.findByName", query = "select f from Fish f where f.name = :fishName"),
        @NamedQuery(name = "Fish.countByFamily", query = "select count(fi) from Fish fi where fi.family.name = :familyName")
}
)
public class Fish implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    private long id;
    @NotBlank
    private String name;
    @Positive
    private int temperature;
    @Positive
    @DecimalMin("0.3")
    private float price;
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="family_fk")
    private Family family;
    @OneToMany(mappedBy = "fish")
    private Collection<Stock> stock;

L’api de persistance est connue par tous. Qui n’a jamais utilisé les annotations @Entity, @Id, @ManyToOne ? D’autres annotations sont venues se glisser dans ce code. Elles proviennent de l’api beans validation. Ces annotations sont utilisables sur n’importe quel objet pas seulement sur les entitys.

CDI

Pour activer l’api d’injection de dépendances, un fichier nommé beans.xml doit être placé dans le répertoire META-INF ou WEB-INF respectivement pour les Jars ou War/Ear. Sans configuration, ce fichier peut rester vide. Ici nous y déclarons un décorator.

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
        bean-discovery-mode="all">
    <decorators>
        <class>com.keywer.article.jakarta.decorator.FishServiceDecorator</class>
    </decorators>
</beans>

Pour injecter une implémentation un simple @Inject fera l’affaire ! CDI ne se résume bien évidement pas à cela, cette API est d’une grande richesse.

@Inject
private FishService fishService;

JAX-RS

Avec CDI certainement l’une des pépites de Jakarta EE, JAX-RS vous permet d’exposer vos services REST. Tout comme CDI, cette api doit être activée en déclarant une classe héritant de la classe Application. C’est à cet endroit que vous pourrez configurer votre api REST.

@ApplicationPath("/petshop")
public class PetShopApplication extends Application {}

Ci-dessous un exemple de ressource jax-rs :

@Valid
@Path("fish")
@ApplicationScoped
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public class FishResource {

    private static final Logger LOGGER = Logger.getLogger(FishResource.class.getName());

    @Inject
    private FishService fishService;

    @Resource(name="concurrent/__defaultManagedExecutorService")
    private Executor executor;

    @GET
    @Path("{fishFamily}")
    public int countByFamily(@NotBlank @PathParam("fishFamily") String fishFamily) {
        return fishService.countByFamily(fishFamily);
    }

    @POST
    @Path("{fishName}")
    public Response buy(@BeanParam FishTransactionParam fishTransactionParam) {
        float bill = fishService.buy(fishTransactionParam.shopName, fishTransactionParam.fishName, fishTransactionParam.quantity);
        return Response.ok(bill).build();
    }

Dans ce projet se trouve des exemples :

Et si on parlait test ?

L’INSTANCIATION PROGRAMMATIQUE

Il est tout à fait possible d’instancier soit même un conteneur Jakarta EE, prenons l’exemple de l’entity manager :

private static EntityManagerFactory entityManagerFactory;
 private static EntityManager entityManager;
 private static ShopRepository shopRepository;

 @BeforeAll
 public static void setUpBeforeClass() {
     Properties properties = new Properties();
     properties.put("eclipselink.persistencexml","META-INF/persistence-h2.xml");
     entityManagerFactory = Persistence.createEntityManagerFactory("JPADemo", properties);
     entityManager = entityManagerFactory.createEntityManager();
     shopRepository = new ShopRepository(entityManager);
 }

 @AfterAll
 public static void tearDownAfterClass() {
     entityManager.close();
     entityManagerFactory.close();
 }

Dans lequel nous faisons référence à un persistence.xml configuré via une base h2.

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="JPADemo" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>com.keywer.article.jakarta.model.Fish</class>
        <class>com.keywer.article.jakarta.model.Family</class>
        <class>com.keywer.article.jakarta.model.FishStockKey</class>
        <class>com.keywer.article.jakarta.model.Shop</class>
        <class>com.keywer.article.jakarta.model.Stock</class>
        <properties>
            <!-- Configuring The Database Connection Details -->
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="javax.persistence.jdbc.password" value="" />
            <property name="javax.persistence.sql-load-script-source" value="META-INF/data.sql"/>
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
        </properties>

    </persistence-unit>
</persistence>

Dans mon cas je fais référence à un fichier persistence.xml présent dans mes resources de tests, j’aurais très bien pu le configurer programmatiquement via un objet properties.

@Test
void it_should_count_fish_by_family() {
    //GIVEN
    String familyName = "Cichlidae";
    //WHEN
    int total = shopRepository.countFishByFamily(familyName);
    //THEN
    assertThat(total, is(4));
}

Le lancement d’un conteneur jpa se fait en moins d’une seconde.

L’UTILISATION DE L’EXTENSION JUNIT 5 WELD

<dependency>
    <groupId>org.jboss.weld</groupId>
    <artifactId>weld-junit5</artifactId>
    <version>2.0.1.Final</version>
    <scope>test</scope>
</dependency>

Cette extension lance un conteneur CDI sans aucune configuration de votre part.

@EnableWeld
public class WeldInjectionTest {

    @Inject
    private ShopRepository shopRepository;

    @WeldSetup
    public WeldInitiator weld = WeldInitiator.from(ShopRepository.class, WeldInjectionTest.class).build();

    @ApplicationScoped
    @Produces
    EntityManager produceEntityManager() {
        return TestUtils.getEntityManagerFactory().createEntityManager();
    }

    @Test
    void it_should_inject_repository(){
        //GIVEN
        Family family = new Family();
        family.setName("Scombridae");
        family.setWaterType(WaterType.SEA);
        //WHEN
        shopRepository.saveFamily(family);
        //THEN
    }
}

Rapide et simple, cette extension vous permettra d’utiliser n’importe quelle fonctionnalité de CDI @Inject @Named @Decorator @Produce etc etc.

SMOKE TEST

Il s’agit de tests fonctionnels ou unitaires de fonctions logicielles critiques.

Un test de fumée répond aux questions suivantes :

MicroShed est une surcouche au framework TestContainer. Il créé et lance pour vous une image Docker de votre application et permet d’interroger de manière programmatique vos endpoints Rest.

@MicroShedTest
public class FishResourceIT {

    private final static String CONTEXT_APPLICATION = "jakarta-article";

    @Container
    public static ApplicationContainer app = new ApplicationContainer("jakarta-article:latest")
            .withAppContextRoot(CONTEXT_APPLICATION);

    @RESTClient
    public static FishResource fishResource;

    @Test
    public void buyExistingFish() {
        //Given
        String existingShop = "Magic Fish";
        String existingFish = "Scalaire";
        FishTransactionParam fishTransactionParam = new FishTransactionParam();
        fishTransactionParam.fishName = existingFish;
        fishTransactionParam.quantity = 1;
        fishTransactionParam.shopName = existingShop;

        //When
        Response response = fishResource.buy(fishTransactionParam);

        //Then
        assertThat(response.getStatus(),is(Response.Status.OK.getStatusCode()));
    }

Le lancement du test nécéssite un environnement disposant d’une instance de docker.

Retrouvez l’intégralité du code sur

Impossible de parler de JakartaEE sans évoquer Eclipse MicroProfile ?

Les MicroProfiles sont également des spécifications détenus par la fondation Eclipse. Ils s’inscrivent dans la continuité des profiles introduits dans JavaEE 6 (Serveur Full, Serveur Web). Il s’agit d’un sous-ensemble de JSR JakartaEE auquels de nouvelles spécifications ont été ajoutées. L’idée est d’être capable de construire un serveur à la carte, composé uniquement de ce dont on a besoin. Pratique pour alléger son image Docker !

De plus il contient des spécifications permettant une meilleur intégration au cloud, tel que le Health Check, la gestion des Configs ou encore les Metrics.

(Les MicroProfiles Eclipse feront l’objet d’un article plus détaillé).

Qu’est-il prévu pour Jakarta EE 9 ?

Cette version est décrite comme la « Tooling release », cette release est prévue première moitié 2020.

1) Le Big-Bang:

Le namespace javax sera transformé en jakarta.

Rassurez-vous, des utilitaires seront fournis pour aider à la migration, notamment un plugin maven.

2) Alignement des implémentations sur Java 11 (derniere LTS)

3) De nombreuses mises à jour:

JSRJakartaEE8=>JakartaEE9
JSF2.33.0
Security1.01.1
JASPIC1.11.2
JACC1.51.7
JAX-RS2.12.2
JEE Concurrency1.11.2
Interceptor1.21.3
Expression Language3.03.1

4) Suppression de JSR
Plusieurs JSR sont en ligne de mire, leur suppression fera l’objet d’un vote.

Vous trouverez le détail de la prochaine release ici

Et pour terminer Jakarta EE 10 la « Feature Release »

Pour cette version prévue fin 2020, rien n’est encore défini. Tout ce que l’on sait c’est qu’elle contiendra de nouvelles JSR et que son développement se fait en parallèle de la version 9.
La première annoncée sera une specification pour le NoSQL proposant une API générique capable d’interroger tous types de base de données NoSQL (Clef-Valeur, Documents, Graph, …)

EN CONCLUSION

  1. Il y aura au minimum une release Jakarta EE tous les ans.
  2. Une release Eclipse MicroProfile sera publiée tous les trimestres.
  3. L’intégralité de ces projets sont totalement OpenSourcés.
  4. Ces serveurs sont plus flexibles, rapides et possèdent une empreinte mémoire réduite !
  5. Vous avez le choix ! De nombreuses implémentations sont disponibles sur le marché.
  6. Tous les géants de l’industrie informatique contribuent; Oracle, RedHat, IBM, Apache, Eclipse, etc

Comptes Twitter à suivre

Références

https://www.eclipse.org/
https://jakarta.ee/
https://www.baeldung.com/java-enterprise-evolution/
https://openliberty.io/
https://www.payara.fish/
https://wildfly.org/
https://projects.eclipse.org/proposals/eclipse-glassfish
http://adam-bien.com/
https://rieckpil.de/
https://github.com/javaee-samples/javaee8-samples
https://developer.mozilla.org/fr/docs/Glossaire/Test_de_fum%C3%A9e
https://microshed.org/
0