I. Introduction▲
EMF permet de générer le code Java à partir d'un modèle Ecore.
En paramétrant un fichier .genmodel, on peut ainsi générer les trois
couches de code : « model », « edit » et
« editor », qui contiennent les sources Java permettant d'exploiter
le modèle métier défini. La couche « model »
contient toutes les interfaces et les implémentations correspondant au modèle
métier. C'est uniquement de cette couche logicielle dont cet article va parler. En
effet, c'est la couche la plus utilisée et la plus représentative du
modèle. Les autres couches (« edit » et
« editor ») sont utilisées pour obtenir des éditeurs
graphiques ou arborescents et ont également leur manière d'être
surchargées. Elles feront probablement l'objet d'un autre article.
Pour surcharger ces trois couches logicielles, la préconisation est d'utiliser la méthode
consistant à modifier directement le code généré en ajoutant un
« NOT » derrière les balises « @generated ».
Le plug-in « mint » (dans EMFTool) (1) , permet de
distinguer les méthodes
surchargées (en rouge), les méthodes ajoutées (en noir) et les méthodes
générées (en bleu) :
Cette méthode peut être acceptable si peu de code est modifié, mais dans le cas de projets où beaucoup de code métier doit être ajouté, il est préférable de :
- séparer clairement le code généré du code développé ;
- générer le code automatiquement dès le process de build ;
- retirer le code généré de la gestion de configuration (CVS, SVN, GIT…) et le régénérer lors de la livraison.
EMF et Eclipse proposent des solutions simples pour répondre à ce besoin.
II. Les étapes de travail▲
La séparation du code généré et du code développé doit être préparée dès le début du projet. Elle consiste en 6 étapes :
- paramétrer le genmodel pour générer le code dans 'src-gen' et nommer correctement les classes et interfaces dans le code généré ;
- créer un répertoire de code source 'src' ;
- créer une nouvelle factory dans le répertoire 'src' ;
- déclarer cette factory en utilisant une extension de 'factory_override' ;
- étendre les classes et les interfaces générées selon les besoins ;
- instancier les nouvelles classes dans la nouvelle factory.
Les cinq dernières étapes sont réalisées par l'outil 'genModelAddon' présenté en fin de cet article qui analyse le fichier « .genmodel » et génère la structure de code attendue. Cet outil est expliqué dans la seconde partie de cet article.
II-A. Règles de nommage▲
Dans l'exemple décrit ci-dessous, nous utiliserons les nommages suivants, où {0} représente le nom de la EClass en cours de génération.
- M{0} : interface générée par EMF ;
- M{0}Impl : implémentation générée par EMF ;
- {0} : interface modifiée par le développeur (étend M{0}) ;
- {0}Impl : implémentation modifiée par le développeur.
II-B. Paramétrage du genmodel▲
Pour travailler proprement, il faut prendre des mesures d'étanchéité, en séparant clairement le répertoire du code généré de celui du code surchargé. Pour ce faire, on paramètre le genmodel de la manière suivante :
II-C. Création d'un répertoire 'src'▲
On crée ensuite un répertoire 'src' (source folder), dédié à la surcharge dans le projet. Le code généré étant stocké dans le 'src-gen' on peut dériver les classes et les interfaces dans le répertoire 'src'. On obtient alors l'organisation suivante :
II-D. Définition de la factory surchargée▲
Ce nouveau répertoire de source va contenir la nouvelle factory EMF pour créer les instances des classes surchargées. Cette factory dérive de la factory générée et propose des créations d'objets avec les nouvelles interfaces :
Afin de faciliter l'écriture du code, on surcharge également l'initialisation de l'eINSTANCE pour récupérer l'instance correcte de la factory. On complétera les méthodes de cette factory au fur et à mesure que l'on surchargera les classes du modèle généré.
II-E. Implémentation de la factory surchargée▲
L'implémentation de la factory est alors simplement :
II-F. Point d'extension 'factory_override'▲
Il ne reste plus qu'à relier cette nouvelle factory à EMF, en utilisant le point d'extension 'factory_override' :
Pour finir, il faut ajouter le code d'initialisation dans l'implémentation de la factory afin d'appeler la prise en compte de cette extension (les méthodes de création des objets, décrites précédemment, ont été condensées pour l'affichage) :
II-G. Utilisation de la factory dans le code▲
Lorsqu'on utilise une factory EMF, l'usage veut qu'on appelle
« MProjectFactory.eINSTANCE ». Cette instance sera
initialisée correctement avec la nouvelle instance de factory, grâce
à l'extension de factory_override. Toutefois, il sera nécessaire de
caster cette instance dans le type ProjectFactory afin de travailler sur les
nouveaux objets dérivés.
On utilisera donc la nouvelle instance initialisée correctement en appelant :
ProjectFactory.eINSTANCE. On remplacera ainsi les appels à
« MProjectFactory.eINSTANCE » par
« ProjectFactory.eINSTANCE ».
II-H. Surcharge du code des interfaces▲
Les interfaces dérivant du code généré par EMF peuvent maintenant contenir des méthodes de confort (add, remove) :
II-I. Surcharge du code des classes▲
On peut alors définir les méthodes de confort et les opérations au même endroit :
III. Générer le code durant le build, dans Eclipse▲
Si on sépare totalement le code généré du code surchargé, il peut être intéressant d'automatiser la génération du code à partir du modèle. EMF nous propose une tâche Ant (emf.Ecore2Java) permettant de générer le code à partir d'un genmodel. Le fichier Ant à écrire est trivial :
Pour l'exécuter, il faudra faire attention d'utiliser le JRE du workspace (paramètre dans le lancement du build, dans l'onglet JRE). Ce fichier ant peut alors être intégré au builder du projet en le mettant en première position pour être sûr que le code généré sera présent pour la compilation :
IV. Le plug-in 'genModelAddon'▲
La mise en œuvre des conseils de cet article peut être fastidieuse à effectuer, car elle est répétitive et doit s'appliquer à chaque classe du modèle. Le plug-in genModelAddon permet de générer toute la structure du code attendu instantanément.
IV-A. Installer le plug-in▲
Les sources du plug-in sont disponibles sur github à
l'adresse :
https://github.com/opcoach/genModelAddon
Pour installer plus simplement le plug-in, un update site à jour est disponible sur la page de
développement du site d'OPCoach :
http://www.opcoach.com/developpement-eclipse/travaux-de-developpement/
Il faut télécharger ce zip et l'installer comme tout update site zippé en
sélectionnant 'Help->Install New Software?' puis 'Add update Site', 'Archive' et
sélection du zip :
IV-B. Utilisation du plug-in▲
Le plug-in ajoute un menu contextuel lorsque le fichier « *.genmodel » est ouvert. Pour l'afficher, il faut faire un clic droit sur l'élément racine du fichier (i.e. le genModel) :
Remarque : il se peut que, dans certains cas, le menu n'apparaisse pas la première fois. Dans ce cas, il faut faire un clic droit sur le Epackage (en dessous), puis refaire un clic droit sur le genModel. Pour générer la structure, il faut donc :
- générer le code dans src-gen en utilisant la commande EMF 'Generate Model Code' ;
- puis générer le code dans src en utilisant la commande 'Generate Derived Source Folder' dans le menu OPCoach.
La commande de génération ouvre un dialogue rappelant les paramètres utilisés dans le genModel ainsi que les paramètres de nommage à utiliser dans le répertoire src. Des tooltips rappellent à l'utilisateur le sens de chaque paramètre :
L'outil ayant pour but de régénérer les squelettes de code dans le répertoire src, il gère également la surcharge des fichiers en proposant à l'utilisateur la sélection des fichiers à générer s'ils existent déjà et en rappelant le code qui sera généré dans le tooltip associé :
En cas de régénération, il est bien sûr conseillé de commiter le code avant, au cas où un fichier serait écrasé suite à un oubli dans le dialogue ci-dessus.
IV-C. Contribution au plug-in▲
Le projet étant hébergé sur github, il est possible d'y contribuer en créant des issues ou en proposant des pull requests :
- la page d'accueil du projet : http://opcoach.github.io/genModelAddon
- la page d'accueil sur github : https://github.com/opcoach/genModelAddon
- le fichier 'pom.xml' pour reconstruire le repository p2 se trouve dans le projet build/com.opcoach.genmodeladdon.repository
Vous pouvez également contacter l'auteur de cet article pour toute remarque ou question en utilisant le mail suivant : olivier.prouvost[at]opcoach[dot]com.
V. Conclusion▲
Dans cet article, nous avons étudié une bonne pratique pour la surcharge du code EMF généré. Nous avons aussi découvert un outil extrêmement pratique pour effectuer cette surcharge de manière automatisée et réduire encore le code écrit à la main et le risque d'erreur.
VI. Remerciements▲
Cet article a été publié avec l'aimable autorisation de la société OPCoachOPCoach qui remercie également Alain BERNARD pour sa relecture et ses remarques. Merci aussi à ced pour sa relecture orthographique.