Skip to main content

Migration du code Open ENT NG vers Open ENT v3

Open ENT v3 s'appuie sur Quarkus et Vert.x 4. Voici les consignes pour migrer votre code vers Vert.x 4 :

  1. Mettre à jour les dépendances :

    • Assurez-vous que les dépendances de votre projet sont mises à jour vers Vert.x 4. Vérifiez votre fichier pom.xml ou build.gradle et remplacez les dépendances Vert.x 3 par leurs équivalents Vert.x 4.
  2. Revoir les changements de l'API :

    • Vert.x 4 a apporté plusieurs changements et améliorations à l'API. Consultez la documentation de l'API Vert.x 4 pour comprendre les nouvelles méthodes et classes. Portez une attention particulière aux changements dans les API principales, telles que Vertx, EventBus et Context.
  3. Migration des mods Vert.x 3 vers des modules Quarkus

  • Vert.x utilise des "mods" pour structurer les composants réutilisables et distribuables. Ces "mods" doivent être re-factorisés pour fonctionner dans l'écosystème Quarkus, souvent en adoptant des conventions Quarkus comme l'utilisation d'annotations CDI pour l'injection de dépendances, la gestion des configurations, etc.

Construction des applications Java

🪛 Open ENT v3 est construite avec Maven pour la partie backend et Yarn pour la partie frontend.

L'utilisation des dernières versions des outils de build sans l'usage des containers pour construire l'application est maintenant possible.

Les images suivantes sont remplacées par des images standards :

OutilsVersionImage Docker Open ENT NGImage Docker Open ENT v3
Node20.5.1opendigitaleducation/node::20-alpine-pnpmnode:lts-alpine3.20
Gradle4.5.1 (Java 8)opendigitaleducation/gradle:4.5.1maven:3.9.8-amazoncorretto-21

Exemple avec maven

docker run -it --rm --name openent -v "$(pwd)":/mnt/ssd/Workspace-PassTech/ent/open-ent -w /mnt/ssd/Workspace-PassTech/ent/open-ent maven:3.9.8-amazoncorretto-21 mvn clean install

Changement de framework

FonctionOpen ENT NGOpen ENT v3
TemplatingMustascheQute
Cryptographiefr.wseduc.webutils.securityBouncy Castle

Migration Mustache vers Qute

FonctionOpen ENT NGQute
I18n{{#i18n}}admin.title{{/i18n}}{i18n:['admin.title']}

Migration des API de Cryptographie

Open ENT NG

BCrypt.hashpw(password, BCrypt.gensalt()));

Open ENT v3

        SecureRandom secureRandom = new SecureRandom();
byte[] salt = new byte[12];
secureRandom.nextBytes(salt);
BCrypt.generate(password.getBytes(), salt, 12));

Migration des APIs Open ENT NG vers Open ENT v3

Open ENT NG

QueryBuilder query = QueryBuilder.start("_id").is(resourceId);

Open ENT v3

new JsonObject().put("_id", resourceId)

Suppression de la dépendance vers la lib MongoDB Helper

Open ENT NG

fr.wseduc.mongodb.MongoDB.parseIsoDate

Open ENT v3

org.entcore.common.utils.DateUtils.parseIsoDate

Open ENT NG

MongoDb.parseDate(date)

Open ENT v3

org.entcore.common.utils.DateUtils.parseIsoDate

Open ENT NG

MongoDb.formatDate(date)

Open ENT v3

org.entcore.common.utils.DateUtils.formatDate

In class fr.wseduc.mongodb.MongDB

Open ENT NG

public void find(String collection, JsonObject matcher, JsonObject sort, JsonObject keys,
Handler<Message<JsonObject>> callback)

Open ENT v3

io.vertx.ext.mongo.MongoClient.findOne(collection, query, Handler<AsyncResult<JsonObject>>)

Open ENT NG

final String prefix = getPathPrefix(config);

Open ENT v3

String prefix = DefaultResourcePath.DIRECTORY_HTTP_ROOT_PATH;

Open ENT NG

this.eb = Server.getEventBus(vertx);

Open ENT v3

  this.eb = vertx.eventBus();

Migration des annotations

Open ENT NG

@ResourceFilter(SuperAdminFilter.class)

Open ENT v3

@Secured(roles = {Roles.SUPER_ADMIN})

Open ENT NG

@ResourceFilter(AdminFilter.class)

Open ENT v3

@Secured(roles = {Roles.SUPER_ADMIN, Roles.ADMIN_LOCAL})

Open ENT NG

UserUtils.getAuthenticatedUserInfos(eb, request)
.onSuccess(userInfos -> {

Open ENT v3

@Inject
JsonWebToken jwt;

UserInfos userInfos = new UserInfos(jwt);

Open ENT NG

notFound(request);

Open ENT v3

return Response.status(Response.Status.NOT_FOUND).build();

Open ENT NG

renderError(request);

Open ENT v3

return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Internal Server Error: " + e.getMessage())
.build();

Open ENT NG

badRequest(request);

Open ENT v3

return Response.status(Response.Status.BAD_REQUEST)
.entity("Query parameter is missing or empty")
.build();

Open ENT NG

@BusAddress("event.store")

Open ENT v3

@ConsumeEvent("event.store")

Migration Vertx 3 vers Vertx 4 Voir Guide

Client Http Vert.x 3

   client.putAbs()

Client Vert.x 4

client.request(HttpMethod.PUT,

Vert.x 3

   eb.send

Vert.x 4

eb.request
final Future<Optional<String>> result = Future.future(); --> Promise<Optional<String>> promise = Promise.promise();
final Future<Void> result = Future.future(); --> Promise<Void> promise = Promise.promise();
Future<Entry<String, String>> future = Future.future(); --> Promise<Entry<String, String>> promise = Promise.promise();
return future; --> return promise.future();
futures.setHandler(res ->....)   ->           futures.onComplete(res ->

Migration du framework Web Utils vers JAXRS

JAX-RS est un framework basé sur Java utilisé pour créer des services web RESTful. Il fait partie de la suite Java EE (Enterprise Edition). Voici quelques adaptations de code avec le framework utilisé par défaut dans Open ET NG web-utils qui fonctionne uniquement avec Vert.x

Web Utils
@GET("/tenant/:id")
public void get(HttpServerRequest request) {
final String id = request.params().get("id");
JAXRS
@Get
@Path("/tenant/{id}")
public void get(HttpServerRequest request, @PathParam("id") String id) {

Migration des singletons Open ENT NG avec Jakarta EE

Pour transformer les classes singletons en un bean CDI avec Jakarta EE (anciennement Java EE), vous devrez procéder à quelques modifications. L'objectif est de tirer parti des fonctionnalités de CDI, telles que l'injection de dépendances, tout en éliminant le besoin d'une méthode getInstance pour gérer le singleton.

Utilisé :

@ApplicationScoped : Cette annotation permet à CDI de gérer le cycle de vie du bean. Une seule instance de JsonSchemaValidator sera créée pour l'application entière, remplaçant ainsi le besoin du modèle Singleton.

@Inject : Cette annotation est utilisée pour injecter automatiquement les instances nécessaires gérées par CDI.

Suppression du Singleton (getInstance) : Le code de gestion du singleton a été supprimé, car CDI gère automatiquement l'unicité de l'instance dans le contexte d'application.

Dépendances gérées par CDI : La gestion des dépendances est maintenant entièrement déléguée à CDI, simplifiant le code et améliorant l'intégration avec le reste de l'application Jakarta EE.

Migration des méthodes init des Verticle Vert.x

Chaque Vert.x Verticle avec Open ENT NG contient une phase d'initialisation permettant de déclarer les controllers et d'initialiser certaines classes partagées par ces controllers.

Les controllers sont remplacés par des Resources JAX-RS et l'initialisation se fait des injections automatiquement.

Exemple Verticle OPEN ENT NG - org.entcore.directory.Directory
@Override
public void start() throws Exception {
final EventBus eb = getEventBus(vertx);
super.start();
MongoDbConf.getInstance().setCollection(SLOTPROFILE_COLLECTION);
setDefaultResourceFilter(new DirectoryResourcesProvider());

rm.get("/userbook/i18n", new Handler<HttpServerRequest>() {
@Override
public void handle(HttpServerRequest request) {
i18nMessages(request);
}
});
final StorageFactory storageFactory = new StorageFactory(vertx, config,
new MongoDBApplicationStorage("documents", Directory.class.getSimpleName()));
final Storage storageAvatar = new FileStorage(vertx, config.getString("avatar-path"),
config.getBoolean("avatar-flat", false), storageFactory.getMessagingClient());
final Storage defaulStorage = storageFactory.getStorage();
WorkspaceHelper wsHelper = new WorkspaceHelper(vertx.eventBus(), defaulStorage);

EmailFactory emailFactory = new EmailFactory(vertx, config);
EmailSender emailSender = emailFactory.getSender();
SmsSenderFactory.getInstance().init(vertx, config);
final JsonObject userBookData = config.getJsonObject("user-book-data");
UserService userService = new DefaultUserService(emailSender, eb, userBookData);
UserBookService userBookService = new DefaultUserBookService(eb, storageAvatar, wsHelper, userBookData);
TimelineHelper timeline = new TimelineHelper(vertx, eb, config);
ClassService classService = new DefaultClassService(eb);
SchoolService schoolService = new DefaultSchoolService(eb).setListUserMode(config.getString("listUserMode", "multi"));
GroupService groupService = new DefaultGroupService(eb);
SubjectService subjectService = new DefaultSubjectService(eb);
ConversationNotification conversationNotification = new ConversationNotification(vertx, eb, config);

DirectoryController directoryController = new DirectoryController();
directoryController.setClassService(classService);
directoryController.setSchoolService(schoolService);
directoryController.setUserService(userService);
directoryController.setGroupService(groupService);
directoryController.setSlotProfileService(new DefaultSlotProfileService(SLOTPROFILE_COLLECTION));
addController(directoryController);
vertx.setTimer(5000l, event -> directoryController.createSuperAdmin());


....

}

Les controllers sont remplacés par des Resources JAX-RS et l'initialisation se fait des injections automatiquement.

Exemple ConfigService OPEN ENT v3 - org.entcore.directory.Directory

@ApplicationScoped
@Startup
public class DirectoryConfigService {

private JsonObject config;

@Inject
public DirectoryConfigService() {
this.loadConfig();
}

private void loadConfig() {
// Supposons que ConfigManager est une classe utilitaire personnalisée qui charge la configuration.
this.config = ConfigManager.getQuarkusConfig("directory");
}

@Produces
@OpenENT("directory")
public JsonObject getConfig() {
return config;
}

@Produces
@OpenENT("userBookData")
public JsonObject getConfigUserBookData() {
return config.getJsonObject("user-book-data");
}

@Produces
@OpenENT("emailSender")
public EmailSender getEmailSender(Vertx vertx, @OpenENT("directory")JsonObject config) {
EmailFactory emailFactory = new EmailFactory(vertx, config);
return emailFactory.getSender();
}

@Produces
@OpenENT("storage")
public Storage getStorage(Vertx vertx, @OpenENT("directory")JsonObject config) {
final StorageFactory storageFactory = new StorageFactory(vertx, config,
new MongoDBApplicationStorage("documents", DirectoryConfigService.class.getSimpleName()));
final Storage storageAvatar = new FileStorage(vertx, config.getString("avatar-path"),
config.getBoolean("avatar-flat", false), storageFactory.getMessagingClient());
final Storage defaulStorage = storageFactory.getStorage();
return defaulStorage;
}
}

Migration des tests unitaires

Les tests unitaires utilisent JUnit 4 ou certaines fois JUnit 5. Les annotations sont adaptées afin d'utiliser le contexte Quarkus et Vert.x

Open ENT NG
@RunWith(VertxUnitRunner.class)
public class FeederTest {

}
Open ENT v3
@QuarkusTest
@TestProfile(CustomTestProfile.class)
public class FeederTest {

}

Chaque test pourra avoir son propre profile avec ses variables d'environnements.

Usage du système de fichiers

Avec Vert.x 3 et les mods, les fichiers contenus dans un mods (un JAT Jar) étaient disponibles dans le répertoire mods. Ils étaient accédé par l'API :

final FileSystem fs = vertx.fileSystem();

Avec Vert.x 4, les fichiers contenus dans les modules quarkus sont conservés dans les librairies et disponibles dans le JAR.

Correspondance des urls

Certaines urls ne sont pas identiques entre Open ENT NG et Open ENT v3. Le nombre est vraiment limité pour simplifier les problèmes de montée de version.

Uniquement les API pour la partie authentification change en raison du passage du module auth vers la solution Keycloak

Open ENT NG - Ancienne url pour se connecter

/auth/login

Open ENT v3 - Nouvelle url pour se connecter

/realms/{{realm}}/protocol/openid-connect/token

Avec une variable realm généralement configurée à "openent"