JBoss Forge est un générateur de code, initialement orienté Java EE. Dans cette série d'articles, nous allons voir comment l'étendre pour générer du code Android !
Pour suivre cet article, il est préférable de connaître l'utilisation de JBoss Forge, de JBoss Developper Studio (JDS)... bref, d'avoir lu l'article précédent consacré à Forge [1].
Le but de cet article est de vous permettre de créer vos propres outils pour vos projets en abordant des points essentiels, utilisés dans un contexte Android.
Ces outils qui vous permettront d'automatiser des tâches répétitives. Donc moins d'ennui au quotidien. Oh joie !
1 Panorama
Avant d'entrer dans le vif du sujet, sachez que désormais Forge peut s'utiliser dans Eclipse, dans IntelliJ Idea et dans NetBeans. Toujours, en sus de la console.
Il y a deux addons que je souhaite porter à votre attention afin de vous faire voir combien les addons peuvent être différents les uns des autres.
Le premier ajoute la synthèse vocale à Forge. Vous pouvez donc l'utiliser pour faire parler vos scripts Forge.
Inutile, donc indispensable :), il vous permet de suivre vos scripts les yeux fermés.
Cet addon est open source et le lien est présent à la fin de l'article [2].
Le deuxième n'est pas open source, mais c'est mon dernier exemple en date d'addon. Dans le cadre d'un projet en cours, je l'ai créé pour automatiser des tâches répétitives de création de classes.
À partir de données de connexion JDBC et d'un nom de procédure stockée, il permet de générer les classes d'entrée (input) et de sortie (output) de cette procédure ainsi que le DAO, le service et leurs interfaces Spring respectives sans oublier l'appel en JPA 2.1. Que du bonheur… une fois que cela fonctionne :)
C'est en fait assez simple. Il suffit d'écrire le code une fois à la main puis de savoir qu'il faut utiliser les metadata de JDBC et le pattern template pour générer ce code.
Comptez quelques heures à feu doux tout de même.
Fin du panorama. À partir de ce grand écart, à vous d'imaginer ce qui vous aiderait dans votre quotidien.
Puisque les addons s'écrivent en Java, et que pléthore de librairies existent, il y a tant de choses à écrire.
De la publication d'un gist directement à partir de l'IDE, ou moins sérieusement mettre à jour son statut FB, envoyer des tweets ou développer une messagerie de groupe. C'est vous qui voyez.
2 Forge, ton core !
Ceci n'est pas un appel à faire plus d'exercice physique, mais un rappel sur le noyau (en anglais core) de Forge. Dans l'article précédent, j'indiquais que Forge (la distribution) est modulaire, car lui-même constitué d'addons (réutilisables), comme Core et UI. Ces addons, vous en avez vu la liste complète lorsque vous avez créé votre premier addon lors de l'article précédent [1].
Les indispensables Core (noyau, sans lui rien n'est possible), UI (qui permet d'interagir avec l'utilisateur via les interfaces) sont les addons desquels dépendront toujours vos addons, mais il y en a d'autres tout aussi utiles :
- Configuration a pour fonction de permettre de stocker de l'information, de façon persistante ;
- Environment permet aussi de stocker des informations, à partager entre addons, mais de façon volatile ;
- Template permet d'utiliser des modèles pour créer des fichiers (voir le design pattern homonyme, ou l'outil Freemarker) ;
- Parser-Java, Parser-XML et Parser-JSON permettent de manipuler des fichiers de chacun de ces types ;
- Resources permet notamment de lire des fichiers.
Ce sont les addons principaux, nous reviendrons sur certains d'entre eux plus avant dans le fil de cet article.
La liste complète se trouve à l'adresse http://forge.jboss.org/addons (cochez la case Core).
Revenons à la création de projet Android.
3 Périmètre
Nous allons revoir comment créer un addon simple (mais non simpliste comme l'avant-goût de l'article précédent) qui créé la structure d'un projet Android.
Parce que je ne suis pas autorisé à remplir tout le magazine avec cet article, le périmètre du code à suivre ne sera pas exactement la création d'un projet identique à celui généré par l'assistant d'ADT (dans Eclipse).
Néanmoins, nous allons voir comment faire saisir des informations à l'utilisateur et les utiliser.
C'est LA brique de base pour tous vos futurs addons.
Nous utiliserons ensuite un addon préexistant, pour créer des fichiers selon un modèle.
4 Rappel – un addon simple
Comme vu dans le précédent article consacré à Forge, il est extrêmement simple d'écrire un addon de type «Au revoir Dennis Ritchie» affichant un simple message à l'écran.
Les étapes sont les suivantes :
- Création d'un projet de type addon, avec la fenêtre Forge (<Control> + <Shift> + <'> …. à priori) ;
- Ajouter les dépendances aux addons Core et UI ;
- Créer une classe ;
- Créer une méthode annotée @Command (lui mettre des attributs value et categories parlants) ;
- Lui faire retourner Results.success(« Meunier, tu dors ?») ;
- Puis, exécuter la commande addon-build-and-install ;
- Et enfin, exécuter la commande créée.
Vous trouverez le code correspondant sur le compte GitHub du magazine.
Battons le fer pendant qu'il est chaud, et commençons notre nouvel addon à partir de ces étapes simples.
Pour rappel, dans ADT, l'assistant pour créer un projet Android ressemble au contenu de la figure 1.
Fig.1 : Premier écran de la création d'un projet Android avec ADT.
Comme vous le voyez, il s'agit de faire saisir quelques informations à l'utilisateur.
Avec Forge, il est très rapide d'obtenir un écran s'en approchant, tel que celui présenté en figure 2.
Fig. 2 : Notre propre écran, forgé à la main. Notez l'enclume en haut à droite.
Pour arriver à ce résultat, le code est le suivant (admirez sa concision) :
public class NewAndroidProject {
String[] PROJECT_FOLDERS = {"assets","bin","gen","libs","res","src"};
@Command(categories={"AndroidSimple"},
help="Help I need somebody",
value="Android::Project")
public Result new_android_project(
@Option(value = "location",label="Répertoire où créer le projet",required=true, type="String",defaultValue="/tmp/projets", enabled=false) String repertoireRacine,
@Option(value = "prjName",label="Project Name",required=true,type="String", description="Le nom du projet",requiredMessage="Il faut un nom",shortName='p') String projectName,
@Option(value = "appName",label="Nom de l'application",required=true,type="String",defaultValue="MillionDollarProject" ) String appName,
@Option(value="package",label="Nom du package Principal",required=true,type="String", defaultValue = "org.wadael.n.million") String packageName,
@Option(value="minSdk", label="Minimum SDK",required=true,type="Long", defaultValue= "16") Integer minSDK,
@Option(value="targetSdk", type="Integer",defaultValue = "21") Integer cibleSDK){
File racine = new File(repertoireRacine + File.separator + projectName);
if(!racine.mkdirs()) // création du répertoire racine du projet
Results.fail("Impossible créer répertoire " + racine );
else{
File f = null;
for(String rep : PROJECT_FOLDERS){
f = new File( racine.getAbsolutePath()+File.separator + rep );
f.mkdirs();
System.out.println("creation du repertoire " + f.getAbsolutePath());
}
}
return Results.success( "Waouh. " + projectName + " " + appName);
}
}
Et là, dans ces quelques lignes, vous pouvez voir qu'il y a non seulement le code pour l'interface utilisateur, mais aussi le code pour créer l'arborescence de répertoires.
Ne nous mentons pas, ce code ne crée pas la totalité d'un projet Android, mais il crée le projet à l'endroit demandé (par défaut /tmp/projets) avec l'ensemble des répertoires de haut niveau qui constituent un projet Android. Toujours pour une question de place, cela n'aurait pas de sens de mettre ici des remplacements de chaînes dans des Strings qui iraient dans des fichiers. Pensons aux forêts. Pour le code façon simpliste, voir GitHub.
La magie qui amène le résultat que l'on vient de voir provient des annotations utilisées. Examinons ces annotations.
La première, apposée sur la classe est :
@Command(categories={"AndroidSimple"}, help="Help I need somebody", value="Android::Project")
L'attribut categories est le nom que l'on retrouve dans la première colonne de la fenêtre Forge. Il est possible d'indiquer plusieurs catégories pour un addon.
Fig. 3 : La fenêtre Forge d'Eclipse.
L'attribut value correspond à la deuxième colonne et au titre de l'assistant, qui est aussi le nom de la commande dans la console Forge !
Rappelez-vous que Forge peut s'exécuter tant en ligne de commandes que dans Eclipse. Aussi, malgré la complétion automatique de la console, évitez les majuscules, les caractères accentués et les espaces dans les valeurs de cet attribut (c’est-à-dire ne faites pas ce qu'indique la figure 3).
Sachez que la valeur sera automatiquement normalisée par Forge, ici « Android::Project » devient « android-project » dans la console Forge. Ce qui est joli dans Eclipse ne l'est pas forcément en ligne de commandes.
Enfin, l'attribut help correspond au sous-titre de l'assistant que l'on vient de forger (voir figure 2).
La deuxième annotation utilisée est @Option dont la première occurrence est :
@Option(value = "location",label="Répertoire où créer le projet",required=true, type="String",defaultValue="/tmp/projets", enabled=false) String repertoireRacine
Les paramètres sont la valeur, le libellé (label), le caractère obligatoire (required=true), le type (Chaîne), et la valeur par défaut.
Valeur et valeur par défaut ? Diantre, n'aurions-nous pas un problème de design ici ? Que nenni l'ami.
La valeur par défaut est donc, dans Eclipse, la valeur initiale du champ ET dans la console, cela sera la valeur utilisée si l'utilisateur ne valorise pas l'attribut. Logique.Tandis que value sera le nom utilisé pour ce paramètre dans la console Forge. Oui, il est différent de label. Mais seul value est obligatoire.
Le nom value est hérité des annotations Java @Option("maValeur") correspondant à @Option(value= "maValeur") en version longue.
Si l'un des champs en required=true n'est pas renseigné, il ne sera pas possible de valider l'écran (car le bouton reste grisé).
D'autres paramètres supplémentaires sont possibles comme dans cet autre exemple :
@Option(label="Project Name",required=true,type="String", value = "ProjectName",description="Le nom du projet",requiredMessage="Il faut un nom",shortName='p') String projectName,...)
Ici, description sera le texte de l'infobulle au-dessus du champ si le pointeur reste dessus.
Pour donner des instructions à l'utilisateur, renseignez l'attribut requiredMessage, qui sera utilisé dans les cas d'absence ou d'erreur de saisie.
L'attribut shortName indique la touche à combiner avec la touche <Alt> pour mettre le curseur de saisie dans le champ correspondant (dans Eclipse). Bonne idée !
Pour compiler le code, ouvrez la fenêtre Forge, et sélectionnez
, puis choisissez le répertoire du projet. Patientez.Ne vous inquiétez pas si la compilation vous semble extrêmement lente. Elle l'est ! Ce n'est pas votre machine qui est devenue particulièrement lente. En fait, la machine bosse beaucoup, car il y a beaucoup de choses à faire faire à la machine pour arriver à un tel niveau de simplicité pour les développeurs. Si vous exécutez Forge dans un Shell en dehors d'Eclipse, la commande est addon-build-and-install.
Exécutez votre addon.
Allez vérifier le contenu du répertoire /tmp/projets/. Il doit contenir un répertoire contenant des répertoires vides. Victoire. Vous voici armé pour développer vos propres addons.
5 Auto critique
En toute fausse modestie, cet addon a un aspect assez formidable. En très peu de code, nous obtenons un comportement voulu, intégré dans l'IDE. Et il est même possible d'avoir plusieurs commandes @Command dans une même classe (l'idéal pour multiplier les commandes simples sans multiplier les classes).
Mais il n'est évidemment pas parfait. Tout y est chaîne de caractères, il n'y a, par exemple, pas de choix dans une liste déroulante possible. Aucune vérification n'est possible et l'on peut saisir Lollipop à la place de 21, ce qui est certes sémantiquement équivalent, mais le projet Android que l'on obtiendrait compilerait beaucoup moins bien.
Ensuite, cette méthode permet de créer seulement un écran de saisie, pas un « assistant ».
Mais cela est possible, voir la classe org.jboss.forge.addon.ui.wizard.UIWizard ainsi que le projet MetaForgeWizard sur mon compte GitHub [3].
Prochainement
Dans un prochain article, nous apprendrons un autre moyen d'écrire des addons, une deuxième façon que je nommerai, à défaut d'un meilleur nom, de mode « moins simple, mais mieux » pour avoir accès à plus de fonctionnalités, en commençant par la validation et l'utilisation d'autres widgets d'UI (liste déroulante, sélecteur de fichier, etc.).
Références
[1] J. Baton, « JBoss Forge2, Java EE facile, très facile », GNU/Linux Magazine n° 180, mars 2015.
[2] Addon de synthèse vocale : https://github.com/forge/forge-speech-addon
[3] Mon compte GitHub :https://github.com/wadael
Je remercie Fabien Dubail, Koen Aers, Horacio Gonzalez (@lostInBrittany) et Philippe Charrière (@k33g_org) pour leur aimable travail de relecture de cet article, ainsi qu'Antonio Goncalves (@agoncal) pour m'avoir fait découvrir l'extensibilité de cet outil, George Gastaldi (@gegastaldi) pour son aide, ma société : DRiMS (@drims_ssii), qui m'a permis de travailler avec cet outil (8h/j >> 3h/nuit) et bien sûr, Sylvie qui s'occupe de nos enfants quand je travaille sur un article.