P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ THERE'S NO PLACE LIKE 127.0.0.1 Articles | Connexion
 
~Créer des tâches pour Ant

Précédent  
  Java  
  Suivant
 Présentation

Ant est l'un des outils les plus utilisés par les développeurs Java. Sa puissance et son ergonomie leur ont permis d'oublier définitivement le vieillissant make. L'une des forces de Ant ? Son extensibilité.
 Sommaire


 Introduction

Si make et ses dérivés ont rendu de grands services depuis de nombreuses années, de nombreuses limitations rendent leur utilisation parfois un peu irritante. Outre la syntaxe très stricte des Makefiles, ces outils se révèlent mal adaptés au développement multi-plateformes. Si cet aspect était relativement peu gênant dans la plupart des cas, il s'avère terriblement handicapant pour Java. Né pour les environnements Unix, make repose entièrement sur l'utilisation des fonctionnalités du shell hôte. Ant est né pour pallier ces limitations et offrir une solution efficace, puissante et portable. Et même si le développeur peut parfaitement exécuter des commandes systèmes à travers le fichier de directives Ant, une nouvelle barrière se dresse devant lui. Que faire si le processus de construction de votre application nécessite un outil disponible sous Linux mais pas sous Windows ? Une solution très simple s'offre à nous : développer notre propre extension, appelée tâche.


 Les tâches Ant

Un fichier de directives est, de manière générale, constitué de cibles elles-mêmes composées de tâches. Ainsi la tâche permet de compiler un ensemble de fichiers source Java. Chacune d'entre elles correspond à au moins une classe dérivant de org.apache.tools.ant.Task. Utilisant habilement les propriétés d'introspection de Java, Ant se charge d'invoquer différentes méthodes de la classe Task en fonction de ce que vous aurez écrit dans le fichier build.xml. Notre exemple va nous permettre de réaliser une nouvelle tâche capable d'exécuter des scripts Python. Nous aurons besoin pour cela de la bibliothèque jython.jar. Le listing 1 présente la tâche la plus simple que nous puissions programmer puisqu'elle se contente de générer une erreur à l'exécution. Avant de l'améliorer, nous allons la déclarer dans notre fichier build.xml afin de pouvoir l'invoquer ultérieurement pour nos essais. Le listing 2 démontre comment exploiter la directive pour définir une nouvelle tâche. Remarquez que nous faisons appel au sous élément pour indiquer à Ant où trouver notre classe, située dans le répertoire lib/ par rapport à la racine du projet, et ses bibliothèques, ici lib/jython.jar. Il est intéressant de noter que l'attribut name vous permet de donner le nom que vous désirez à la tâche. Ceci vous permet également d'en renommer une autre. Vous pouvez à présent essayer votre travail :

<target name="main">
  <python />
</target>
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Vous devriez obtenir une magnifique erreur prouvant que tout a, paradoxalement, fonctionné correctement. La prochaine étape va consister à permettre à l'utilisateur de rédiger un script Python exécutable :

<python>
print "Nicolas Herry a décidé de devenir la doublure d'Harry Potter"
print "=" * 40
</python>
       
      
JextCopier dans Jext
Lorsque le parser Ant découvre des données pour un élément il invoque la méthode addText(String) de la tâche. Il nous suffira donc de conserver le script en mémoire lors de cet appel avant de l'interpréter dans la méthode execute(). Nous devons donc utiliser le PythonInterpreter de la bibliothèque Jython. Le problème qui se pose alors est de savoir quand l'instancier. Nous pourrions le faire dans la méthode execute() mais une tâche peut être invoquée plusieurs fois. Nous pouvons heureusement surcharger la méthode init() qui sera invoquée lors de la création de la tâche. Remarquez qu'une tâche initialisée n'est pas nécessairement exécutée par la suite. Le listing 3 présente les modifications apportées au code source. La méthode setInterpreter() n'est pas reproduite ici mais sachez simplement qu'elle utilisent le principe de l'instanciation paresseuse combinée au motif de conception singleton. De fait, toutes les instances de PyAntTask ne partagent qu'un seul interpréteur. Et voilà, vous pouvez à présent exécuter des scripts Python lors de vos compilations, constructions et déploiements d'applications. La réalisation d'une nouvelle tâche se révèle particulièrement simple mais nous n'en avons pas encore exploité toutes les possibilités.


 Tâches complexes

Bien que très utile, notre nouvelle tâche souffre d'un grave défaut puisqu'elle ne permet pas d'exécuter un script existant se trouvant dans un fichier externe. Nous allons donc y remédier en ajoutant un attribut à notre tâche :

<python script="nherry.py" />
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Pour chaque attribut rencontré, Ant invoque un mutateur correspondant dans votre classe. Celui-ci doit s'intituler setNomAttribut et doit accepter un paramètre. Le type de ce dernier est laissé à votre entière appréciation puisqu'une conversion sera automatiquement réalisée en fonction de vos besoins. Ainsi les valeurs "true", "yes" et "on" pourront être converties en un booléen vrai. Nous vous conseillons de vous reporter à la documentation pour obtenir des informations complètes à ce sujet, notamment à propos certains types inhérents à Ant. Le type le plus approprié à notre exercice s'avère bien entendu java.io.File. Voici le mutateur que nous utiliserons :

private File scriptName;
public void setScript(File scriptName)
{
  this.scriptName = scriptName;
}
       
      
JextCopier dans Jext
Prenez garde lorsque vous utilisez le type File. Celui fonctionne relativement à la racine du projet et non relativement au répertoire d'exécution de Ant. Si vous exécutez build.xml dans le répertoire ~/pyant/src mais que la racine du projet correspond à ~/pyant/, la valeur script="nherry.py" correspondra à ~/pyant/nherry.py. Si nous remplacions le type File par une chaîne de caractère, nous devrons agir relativement au répertoire d'exécution. Nous pouvons également imaginer que l'utilisateur désire rédiger plusieurs scripts pour les invoquer en divers endroits du code, sans pour autant les placer dans un fichier externe. Une solution simple pourrait ressembler à cet exemple :

<python>
  <action name="snippet1">
    print "Action 1"
  </action>
</python>

<python call="snippet1" />
       
      
JextCopier dans Jext
Dans ce cas, le nom de l'élément imbriqué, ici , sera fixe. Lorsque le parser en trouvera un, il cherchera à invoquer l'une des méthodes suivantes :

public NestedAction createAction();
public void add(NestedAction a);
public void addConfigured(NestedAction a);
       
      
JextCopier dans Jext
La première méthode vous permet de vous occuper vous-mêmes de la construction d'une instance de NestedAction (de manière générale de NestedNomElement). Les deux suivantes nécessitent la présence des constructeurs NestedAction() et NestedAction(Project p) mais permettent à Ant de faire le travail de création à votre place. Enfin, addConfigured() ne sera appelé qu'après configuration de l'élément imbriqué, c'est-à-dire après invocation des méthodes relatives à ses attributs et à son contenu. Ces dernières suivent les mêmes règles que pour les tâches.


 Listings

Listing 1

import org.apache.tools.ant.*;

public class PyAntTask extends Task
{
  public void execute() throws BuildException
  {
    throw new BuildException("Tâche inactive.");
  }
}
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Listing 2

<taskdef name="python" classname="PyAntTask">
  <classpath>
    <pathelement location="lib/jython.jar" />
    <pathelement path="lib" />
  </classpath>
</taskdef>
       
      
JextCopier dans Jext
Listing 3

import org.python.core.*;
import org.python.util.PythonInterpreter;
// .
private static PythonInterpreter parser;
private String pythonCode = null;

public void init()
{
  super.init();
  setInterpreter();
}

public void addText(String text)
{
  this.pythonCode = text;
}

public void execute() throws BuildException
{
  if (pythonCode == null)
    throw new BuildException("Aucun script à exécuter.");
  parser.exec(pythonCode);
}
       
      
JextCopier dans Jext

 Téléchargement

Vous pouvez téléchager l'exemple pyant pour suivre l'article.



par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006


Précédent  
  Java  
  Suivant

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE