Affichage des articles dont le libellé est emf. Afficher tous les articles
Affichage des articles dont le libellé est emf. Afficher tous les articles


JET2, ou comment faire du vieux avec du neuf...

Comme promis dans mon précédent article sur Jet, j'ai fais quelques recherches sur le tout nouvel outil Jet2 du projet EMFT d'Eclipse, qui est annoncé comme le successeur de Jet. Je dois avouer que j'en reste sur ma faim...

Tout d'abord, quelles sont les nouveautés proposées par Jet2?

Fonctionnellement, trois choses :

  • les templates portent maintenant clairement l'extension ".jet" et ne contiennent plus que le code à générer (plus d'en-tête Jet comme avant...)
  • l'exécution de la génération nécessite un point d'entrée (généralement un fichier "main.jet") qui déclare les templates à parcourir ainsi que les informations portées auparavant par l'en-tête des templates
  • les sources de données sont maintenant sous forme de fichiers xml, sans contrainte aucune. Les templates utilisent XPath pour y récupérer les informations.

Techniquement, trois changements (et c'est là que le bât blaisse...) :

  • le système Jet2 est dépendant du framework Eclipse (!!) : il est donc impossible de l'utiliser de manière "headless", a l'extérieur d'un environnement Eclipse, comme pouvait le faire Jet.
  • du fait du point précédent, le moteur qui opère pour travailler les templates est devenu une boite noire, impossible de simplement appeler la méthode "generate()" de la classe produite ou du processeur principal.
  • un système de taglib a été mis à disposition au sein des templates pour aider à l'écriture (itération, accesseurs, tests...)

Pour résumer, jusqu'ici je ne vois pas vraiment de réelles nouveautés, ou en tout cas de quoi vraiment attiser ma curiosité. J'ai tout de même cherché à mettre un peu en pratique tout cela pour voir vers quel(s) concept(s) ce système s'oriente. Pour essayer de faire un parallèle avec l'exemple que nous avions vu pour Jet, j'ai ici voulu créer une classe qui affiche le nom du projet dans lequel elle se situe... Voici donc la structure de base d'un nouveau projet Jet2 :

Comme je l'ai indiqué plus haut, on retrouve le fichier "main.jet", point d'entrée de la transformation. Son contenu est le suivant :

<%@taglib prefix="ws" id="org.eclipse.jet.workspaceTags" %>
<%-- Main entry point for jet2Project --%>


    

Comme on le voit, il pointe sur le fichier "projectIntrospectionTemplate.jet" qui contient le code ma classe. L'attribut "path" indique le fichier de destination (à la place de l'en-tête Jet)

Le contenu de mon template :

package com;
public class ProjectIntrospection {
 public static void main(String[] args){
  System.out.println("This class project name : ");
 }
}

On voit ici que le contenu est épuré de toute meta-information pour le moteur. Puisque je cherche à connaitre le nom du projet, j'ai fournit comme source de données au template le fichier ".project" d'Eclipse (qui n'est ni plus ni moins que du xml) :



 jet2Project
 
 
 
 
...

La balise "c:get" de mon template permet d'accèder grâce à XPath à la valeur du nom du projet.

Pour lancer la transformation, il faut créer une Run Configuration :

C'est ici qu'il faut indiquer la source de données. Elle sera automatiquement passée en paramètre du moteur. Voici donc le résultat :

package com;
public class ProjectIntrospection {
 public static void main(String[] args){
  System.out.println("This class project name : jet2Project");
 }
}

Au final, rien de vraiment fulgurent... Ce que je regrette donc le plus dans ce nouveau système est le fait qu'il soit dépendant du framework Eclipse, je ne comprends pas vraiment l'intérêt. De plus, je doute du réel "plus" fournit pas l'utilisation des balises : cela contraint à l'emploi de fichiers xml en entrée et par dessus tout, cette technique me semble empiéter beaucoup sur le territoire de Groovy. Pour ceux qui n'en ont pas entendu parler, je vous conseille d'aller lire quelques articles sur cet outil et vous vous apercevrez rapidement à quel point Jet2 semble s'en rapprocher.

J'invite donc tous ceux qui ont déjà travaillé avec Jet2 à donner leur avis, car j'ai l'impression qu'avec cette nouvelle version, la philosophie première de Jet (en tant qu'outil "pragmatique") se perd un peu...

Sources

  1. http://www.ibm.com/developerworks/opensource/library/os-ecl-jet/
  2. http://help.eclipse.org/ganymede/index.jsp?topic=/org.eclipse.platform.doc.user/tasks/antRunner.htm : Une tâche Ant qui permet de s'extraire de l'interface graphique d'Eclipse
  3. http://weblogs.java.net/blog/2009/01/04/template-code-generator-apache-velocity-jet-jet2
  4. http://www.linuxtopia.org/online_books/eclipse_documentation/eclipse_jet_development_guide/index.html

Fichier(s) joint(s) :



A la découverte de Jet, un outil pour "Pragmatic programers"

Ayant potassé dernièrement le livre « Pragmatic programers » de Andrew Hunt et David Thomas (que je recommande vivement), j'ai trouvé bon de l'utiliser comme introduction de cet article, particulièrement le chapitre sur les générateurs de code dont le leitmotiv est :

« Write Code That Writes Code : Code generators increase your productivity and help avoid duplication. »

Ainsi, j'ai décidé de faire ici une présentation de Jet (Java Emmitter Templates), un outil du projet EMF d'Eclipse dont le but est de faciliter la génération de code. Son fonctionnement est le suivant : il se base sur des fichiers "templates" pour produire n'importe quel type de contenu (xml, java, html) par l'intermédiaire de classes Java.

J'ai choisi, pour cet article, de mettre en pratique Jet dans le cadre d'un plugin Eclipse ajoutant un builder à un projet web, qui créera la page d'accueil du site en fonction de certaines caractéristiques du projet... Tout un programme !

Il faut donc pour commencer, mettre en place un plugin Eclipse constitué d'un builder et d'une nature, qu'il sera possible d'ajouter/supprimer sur un projet du workspace. C'est ce qu'explique la source (5), à l'aide de l'assistant pour le point d'extension en question. En suivant ce tutoriel, vous devriez arriver à mettre en place ce système, comme je l'illustre ici :

A ce stade, le lancement du plugin doit aboutir à l'ouverture d'une nouvelle application Eclipse à l'intérieur de laquelle, si vous créez un projet de n'importe quel type, vous disposerez dans le menu contextuel d'une action pour ajouter/supprimer la nature en question et de ce fait le builder que nous venons de mettre en place.

Il faut maintenant ajouter à notre plugin la capacité de gérer les templates Jet. Pour ce faire, il faut aller dans l'assistant de création d'un nouveau projet, choisir "Converts projects to JET Projects" et sélectionner le plugin.

La première conséquence est l'apparition du dossier "templates" dans notre projet : c'est lui qui accueillera nos fichiers jet. La première chose à faire est de configurer le moteur Jet afin de lui indiquer où générer ses fichiers. Dans les propriétés du projet, on trouve maintenant une section "jet settings" : il faut ici spécifier au moteur de produire ses fichiers au sein de notre classpath, donc dans le dossier "src".

Les différents packages contenant les classes générées seront indiqués plus tard, à l'intérieur même des templates.

Nous pouvons dès à présent écrire le template de notre page d'accueil. Dans le dossier "templates", le fichier "index.htmljet" contient :

<%@ jet package="jetTemplates" class="GeneratedIndex" imports="org.eclipse.core.resources.IProject" %>
<% IProject project = (IProject) argument; %>
... en-tete html ...

I was generated by Pef for Jet tutorial in project <%=project.getName()%>!

...

Le premier attribut "package" de l'en-tête jet (première ligne) indique le nom du package dans lequel sera générée la classe correspondante à ce template, dont le nom est spécifié dans l'attribut "class". Le paramètre implicite "argument" représente le projet du workspace sur lequel s'exécutera le builder.

Une fois ce fichier écrit, nous disposons d'une nouvelle classe, "GeneratedIndex.java", dont nous allons nous servir pour créer physiquement la page d'accueil. Pour ce faire, voici le code à ajouter au builder :

package jetbuilderplugin.builder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import jetTemplates.GeneratedIndex;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;

public class Builder extends IncrementalProjectBuilder {

 public static final String BUILDER_ID = "JetBuilderPlugin.pefBuilder";

 /*
  * (non-Javadoc)
  * 
  * @see org.eclipse.core.internal.events.InternalBuilder#build(int,
  * java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
  */
 protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
   throws CoreException {
  if (kind == FULL_BUILD) {
   fullBuild(monitor);
  } else {
   IResourceDelta delta = getDelta(getProject());
   if (delta == null) {
    fullBuild(monitor);
   } else {
    incrementalBuild(delta, monitor);
   }
  }
  return null;
 }

 protected void fullBuild(final IProgressMonitor monitor)
   throws CoreException {
  System.out.println("full build!");
  GeneratedIndex gc = new GeneratedIndex();
  String generatedString = gc.generate(getProject());
  IFolder folder = getProject().getProject().getFolder("WebContent");
  String nomFichier = "index.html";
  ecritureFichier(folder, nomFichier, generatedString, monitor);
 }

 protected void incrementalBuild(IResourceDelta delta,
   IProgressMonitor monitor) throws CoreException {
  System.out.println("incremental build!");
 }

 private void ecritureFichier(IContainer container, String nomFichier,
   String contenu, IProgressMonitor monitor) throws CoreException {
  if (!container.exists() && container instanceof IFolder) {
   IFolder folder = (IFolder) container;
   monitor.subTask("Creation du dossier " + folder.getName());
   prepareFolder(folder, monitor);
  }
  try {
   InputStream stream = new ByteArrayInputStream(contenu.toString()
     .getBytes());
   IFile file = container.getFile(new Path(nomFichier));
   if (file.exists()) {
    file.setContents(stream, true, true, monitor);
   } else {
    file.create(stream, true, monitor);
   }
   file.setDerived(true);
   stream.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }

 private void prepareFolder(IFolder folder, IProgressMonitor monitor)
   throws CoreException {
  IContainer parent = folder.getParent();
  if (parent instanceof IFolder) {
   prepareFolder((IFolder) parent, monitor);
  }
  if (!folder.exists()) {
   folder.create(true, true, monitor);
  }
 }
}

Le plugin est terminé, lançons maintenant l'application finale et créons le projet web. La génération de notre page d'accueil est automatiquement lancée à la création du projet ou à chaque "clean" (puisque positionnée dans le "fullBuild" du builder) :

Il suffit maintenant de lancer le projet web avec une instance de Tomcat pour voir la page d'accueil suivante :

On voit clairement que la page d'accueil indique bien le nom du projet d'où elle provient ! Je vous laisse donc imaginer les possibilités offertes par ce système : génération automatique de squelettes d'applications, réutilisation de code...

J'ai volontairement survolé les détails de cette mise en place de Jet pour plutôt donner un aperçu de tout ce qui est envisageable. Les sources (1) et (2) vous permettrons d'entrer plus dans les détails de l'implémentation.

Pour finir, je précise tout de même que cet article présente la première version de Jet utilisée dans EMF. J'ai récemment observé qu'une nouvelle version a été mise à disposition dans le cadre du "nouveau" projet EMFT, surnommée JET2 et dont le fonctionnement est légèrement différent puisqu'il offre de nombreuses nouvelles possibilités. Le temps de me renseigner plus à ce sujet et je publierai un article décrivant toutes ces nouveautés.

Source

  1. http://www.eclipse.org/articles/Article-JET/jet_tutorial1.html
  2. http://www.vogella.de/articles/EclipseJET/article.html
  3. http://www.linuxtopia.org/online_books/eclipse_documentation/eclipse_jet_development_guide/index.html
  4. http://www.eclipse.org/modeling/emf/docs/architecture/jet2/jet2.html
  5. http://www.eclipsepluginsite.com/builders-natures-markers.html

Fichier(s) joint(s) :