vendredi 29 novembre 2013

Tester son application JavaEE avec Arquillian

La bonne pratique de faire des tests unitaires dans son application est rentrée dans les moeurs du développeurs Java, JUnit étant le plus connu et utilisé. Il permet de répondre facilement aux cas de tests standards. Mais les problématiques arrivent très vite dès lors que l’on souhaite tester en environnement EE : comment effectuer des tests unitaires sur les EJB, sur les services d’accès aux données, comment prendre en compte l’aspect transactionnel et les données sans polluer une base existante ?

Autant de questions qui peuvent trouver une réponse grâce à Arquillian, un framework de test pour JavaEE.


Arquillian permet de tester son code dans un environnement proche des conditions réelles de l’application, c’est à dire au sein d’un conteneur JavaEE. A ce titre, Arquillian propose différents type de conteneurs pour ces tests.

Quelle différence entre le mode de conteneur “Embedded”, “Managed” et “Remote” ?
On peut dissocier le mode embarqué (Embedded) du mode “In-container” (Managed ou Remote).
Le mode embarqué (Embedded) est une “émulation de conteneur” : le process du mode embarqué est exécuté dans le même process que le test et agit comme un conteneur mais n’en est pas un. Il y’a donc des risques de différences de comportement entre ce mode et le conteneur réel. De plus, le mode embarqué se réduit à l’injection CDI : certaines fonctionnalité ne sont pas prises en compte (JPA) réduisant son intérêt dans certains cas.

Le mode in-container (Managed ou Remote) tire parti d’un conteneur réel qui peut être distant ou managé.
Le mode distant (Remote) utilise un conteneur hors de l’environnement de test. Ce mode présume que le conteneur soit déjà démarré : lors de l’exécution du test unitaire, Arquillian n’a pas la main sur le démarrage/arrêt du conteneur. C’est donc au développeur de s’assurer que le conteneur soit démarré avant de lancer ses tests. Le mode embarqué est conseillé en phase de développement pendant lequel le développeur peut placer le conteneur en Debug et ainsi faciliter l’analyse et la correction de bugs.

Le mode managé (managed) est simplement un conteneur distant qui prend également en compte son cycle de démarrage et d’arrêt. Contrairement au conteneur embarqué, le processus du conteneur managé est exécuté dans un process JVM séparé. Une fois que le serveur est démarré, il se comporte comme le conteneur distant, c’est à dire qu’Arquillian intéragit avec lui à travers d’un protocole distant (comme HTTP).
Le conteneur managé est plus utile dans le cadre d’un environnement d’intégration continue dans lequel l’on souhaite tester sur un conteneur complet, mais sans savoir s’il est déjà démarré. Arquillian s’occupe donc de démarrer le conteneur avant d’effectuer les tests, puis de l’éteindre une fois qu’ils sont terminés.

micro-déploiement par classe de tests

L’utilisation d’Arquillian sur les cas classiques est très bien expliqué sur le tutoriel du site, je ne vais donc pas m’attarder sur le sujet et redirige donc vers le site officiel : http://arquillian.org/guides/getting_started/ beaucoup mieux expliqué que je ne le ferais, je vous conseille donc d'y jeter un oeil.

En résumé, pour réaliser un test Arquillian, il faut créer une classe de tests annotée @RunWith(Arquillian.class). Cette annotation précise à JUnit d’utiliser Arquillian comme controleur de test. Arquillian recherche ensuite toutes les méthodes annotées avec @Deployment pour récupérer l’archive de test que l’on appelle le micro-deploiement. Le but de ce micro-déploiement est d’isoler les classes et les ressources qui sont utilisées pour le tests.

Contrairement à un test normal JUnit, Arquillian ne s’appuie pas sur l’intégralité du Classpath. Il faut indiquer seulement les ressources dont nous avons besoin pour les tests. L’archive est crée avec Srinkwrap, l’API de création d’archive (JAR, EAR, WAR). Cette approche permet de concentrer les tests exclusivement sur les classes utiles et permet de garder des tests propres et maintenables.
@Deployment
public static WebArchive createDeployment() {
  
    WebArchive webArchive = ShrinkWrap.create(WebArchive.class)
                .addAsWebInfResource("META-INF/ejb-jar.xml")
                .addAsWebInfResource("META-INF/beans.xml")
                .addPackages(true, "fr.lynchmaniac.arquilliantest");
    return webArchive;
}
Le composant à tester est injecté en annotant l’attribut @EJB ou @Inject.
@EJB
WeatherWs ws;
Le positionnement des annotations @Test définir les méthodes de tests constitue les méthodes de tests unitaires. Sur ce dernier point, les tests Arquillian ressemblent comme deux gouttes d'eau à un test JUnit.
@Test
public void rainingTest() {
    Assert.assertFalse(ws.isRainingInAnHour("Nantes"));
}
Comme expliqué précédemment, il est préférable d’utiliser le mode Remote ou Managed. Dans notre cas, nous utilisons JBoss 7.1.1, les dépendances tirées sont donc, pour le mode Managed :
<dependency>
    <groupid>org.jboss.as</groupid>
    <artifactid>jboss-as-arquillian-container-managed</artifactid>
    <version>7.1.1.Final</version>
    <scope>test</scope>
</dependency>
Et pour le mode remote, nous utilisons :
<dependency>
    <groupid>org.jboss.as</groupid>
    <artifactid>jboss-as-arquillian-container-remote</artifactid>
    <version>7.1.1.Final</version>
    <scope>test</scope>
</dependency>
A noter que les artefacts suivants ne sont pas disponibles par défaut sur le dépôt Central Maven. Il faudra ajouter les dépôts suivants :
<repository>
    <id>jboss.org</id>
    <url>https://repository.jboss.org/nexus/content/repositories</url>
</repository>
    
<repository>
     <id>JBoss 3rd Party Releases</id>
  <url>https://repository.jboss.org/nexus/content/repositories/thirdparty-releases/</url>
</repository>

Quelques conseils : 
  • Attention à n’ajouter que ce dont vous avez besoin au micro-déploiement. Si vous avez besoin d'une classe, n'ajoutez pas le package complet. En effet, ajouter trop de classe dans une archive augmente de temps de création et de déploiement. 
  • Les classes de tests (i.e. celles qui lancent les tests), n’ont pas besoin d’être présente dans l’archive, veillez à les exclure du micro-déploiement. 
  • Attention à bien définir la variable d’environnement JBOSS_HOME qui est nécessaire, en mode Managed pour lancer le serveur. 
  • Réduire au maximum le chargement des modules JBoss afin d’en réduire autant que possible le temps de chargement. Il peut être utile de créer son propre standalone.xml utilisé exclusivement pour le passage des TU. Dans notre cas de tests, nous avons réduit au chargement des modules suivants dans JBoss: {org.jboss.as.} configadmin,connector, ee, ejb3, jmx, jpa, logging, naming, osgi, remoting, security, threads, transactions, web, webservices,weld 
  • Définir un profil Maven de développement, actif par défaut, qui inclus l’artefact pour le déploiement Arquillian en mode Remote. Un profil "ci" peut être créé pour inclure l’artefact en mode "managed".
  • Dans le cas pour lequel vous souhaiter intégrer des artefacts projet à votre micro-déploiement via le Dependency Resolver, veillez à garder en tête les phases du cycle de vie Maven : test -> package -> install. Si vous souhaitez tirer parti d’un artefact pendant les tests, il faudra prévoir un passage de tests en 2 étapes : installer d’abord l’artefact en skippant les tests (mvn install -DskipTests) pour qu’il soit présent dans le dépôt, puis ensuite effectuer le test (mvn test).

Micro-déploiement par ensemble de classe de tests

Cette approche assure un premier niveau de test basique et fonctionnel. Il s’avère que sur un projet plus conséquents, comportant un grand nombre de classes de tests, cette approche s’avère très longue lors du passage des TU. En effet, chaque classe de test définit son propre micro-déploiement, mais nécessite également des phases de déploiement et de retrait des archives (en l'occurrence très souvent des WAR). Il s’agit clairement d’une limitation dans notre cas, car la création et le déploiement d’une archive est très coûteuse en temps. De ce fait, un grosse partie du temps de passage des TU est passé dans les tâches qui pourraient être mutualisées : création, déploiement et retrait de l’archive.


 En effet, pour des raisons d’optimisation, il est plus judicieux, d’augmenter le nombre d'éléments dans le micro-déploiement (qui sera un peu plus gros), mais d’assurer cette création et ce déploiement une seule fois pour tous les tests. Nous laissons tomber le déploiement par classe de test mais assurons un déploiement par unité " fonctionnelle ". L’objectif est donc de mutualiser la création et le déploiement afin d’éviter qu’il y ait un surcoût pour chaque nouveau test.

 Nous utilisons une extension d'Arquillian afin de détourner la cinématique de test à des fins d'amélioration de temps de passage des tests. La cinématique de test est donc la suivante :


 Arquillian ne propose pas cette fonctionnalité par défaut à l’heure actuelle (prévue dans la Roadmap de la version 2.0), mais nous allons voir comment tirer partie des extensions pour effectuer un micro-déploiement mutualisé pour une Suite de test.

Définition de la classe d’extension.
La classe d’extension ArquillianSuiteExtension est une classe technique qui implémente org.jboss.arquillian.core.spi.LoadableExtension. Je n’entre pas en détail dans l’implémentation de cette classe, les sources sont disponibles sur Github. Elle intercepte les évènements du cycle de vie d’Arquillian (démarrage, deploy, etc.). L’idée derrière cette classe est d’intercepter le démarrage d’un test Arquillian et de forcer à constituer le déploiement à partir d’une classe spécifique, au lieu de le lire dans chaque classe de test.
La classe contenant le déploiement est indiqué dans le fichier de configuration arquillian.xml :
<extension qualifier="suite">
    <property name="deploymentClass">fr.lynchmaniac.test.Deployments</property>
</extension>

Dans notre exemple, le nom qualifié ici fr.lynchmaniac.test.Deployments désigne la classe chargée de mutualiser la création de l’archive de test pour l’ensemble du projet.

Enregistrement de la classe d’extention
Pour être prise en compte, la classe d’extension doit enregistrée. C’est SPI qui est utilisé pour prendre en compte cette extension. le fichier META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension doit être présent dans le CLASSPATH et contenir le chemin vers la classe d’extension :
fr.lynchmaniac.test.ArquillianSuiteExtension

Classe de définition du micro-déploiement
La classe fr.lynchmaniac.test.Deployments doit contenir une méthode annoté @Deployment qui constituera le micro-déploiement complet pour un domaine. 

Attention : contrairement au premier chapitre, dans le cadre d’un déploiement mutualisé, les classes de tests doivent être présentes dans l’archive du micro-déploiement.

 @Deployment
    public static WebArchive deploy() {
    
     WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "ARQUILLIAN-TEST.war");
        
        webArchive.addPackages(true, "fr.lynchmaniac.arquilliantest")
                // dans ce cas, la classe de test doit être inclus dnas le WAR !
                .addClass(WeatherWsTest.class)
  .addClass(WeatherWs2Test.class)
                .addAsWebInfResource("META-INF/ejb-jar.xml")
                .addAsWebInfResource("META-INF/beans.xml");
 
     return webArchive;

    }

Les classes de tests
Cette classe ne doit surtout pas contenir d'annotation @Deployments, mais doit posséder au moins une annotation @Test.
@RunWith(Arquillian.class)
public class WeatherWsTest {

    @EJB 
    WeatherInfoService wis;
 
    @Test
    public void rainingTest() {
        Assert.assertFalse(wis.isRainingInAnHour("Nantes"));
    }
}

En conclusion

Ce premier article sur Arquillian, réalisé conjointement avec Vincent PIARD, a été rédigé dans l’objectif de donner un retour d’expérience sur les problématiques rencontrées pour les tests unitaires sur des architectures JavaEE. Nous avons utilisé Arquillian depuis plus d’un an sur un projet complexe et ce framework nous a aidé à les résoudre en partie.

En dernier conseil, il est important de garder à l’esprit plusieurs éléments essentiels pour faciliter les développements de tests unitaires dans ce cas :

  • Assurer un couplage lâche des composants est essentiels pour le passage des tests. Ceci permet de faciliter la création de mock et d’assurer une meilleure isolation des tests. 
  • Ne pas sur-découper les projets dans l’IDE et les artefacts (JAR en l’occurence). Un découpage fonctionnel ne veut pas obligatoirement dire un découpage technique" (i.e. un projet IDE/maven différent). Avoir trop de découpage en sous module technique complexifie les dépendances et le développement dans l'IDE. Cette complexité rend la constitution des micro-déploiements difficile à gérer.
  • Il est illusoire de penser que tous les TU doivent avoir une couverture de test de 100%. Vouloir atteindre 100% de couverture de test est illusoire et coûtera bien trop cher par rapport à la valeur ajoutée ! Préférez une approche réaliste : rester dans une fourchette de 2 à 5 tests. L’idéal est à minima de tester avec des entrées vides et au minimum et de tester avec des entrées complètes (cas à vide et cas optimal).
Dans le prochain article, nous verrons comment tirer parti d’Arquillian pour effectuer des tests de persistances.

Pour aller plus loin


Pour voir le détail de l'implémentation notamment toute la mécanique technique portée par ces tests, je vous encourage à jeter un oeil aux sources disponibles sur Github.

Autres sources intéressantes :

jeudi 10 octobre 2013

Formater du code Java avec JDT

Je continue ma série d'article sur l'industrialisation des générateurs de code commencé précédemment.
Dans l'article précédent je présentais comment analyser la syntaxe d'un code source Java afin d'effectuer un premier niveau de validation.
Dans cet article, nous allons voir comment formater son code de manière programmatique (en standalone), conformément à un formateur Eclipse. On va voir comment utiliser Eclipse JDT pour nous aider.

Mise en place du projet

Pour l'utilisation du formateur d'Eclipse, je m'appuie sur les librairies disponibles dans Eclipse Kepler. J'utilise les librairies suivantes :
  • org.eclipse.jdt.core_3.9.1.v20130905-0837.jar
  • org.eclipse.jface_3.9.1.v20130725-1141.jar
  • org.eclipse.jface.text_3.8.101.v20130802-1147.jar
  • org.eclipse.core.runtime_3.9.0.v20130326-1255.jar
  • org.eclipse.equinox.common_3.6.200.v20130402-1505.jar
  • org.eclipse.text_3.5.300.v20130515-1451.jar
  • org.eclipse.osgi_3.9.1.v20130814-1242.jar
  • org.eclipse.core.resources_3.8.101.v20130717-0806.jar
  • org.eclipse.core.jobs_3.5.300.v20130429-1813.jar
  • org.eclipse.core.contenttype_3.4.200.v20130326-1255.jar
  • org.eclipse.equinox.preferences_3.5.100.v20130422-1538.jar

Il est possible de formater son code Java en utilisant l'API directement depuis la ligne de commande. C'est ce qu'explique Peter Friese dans son article Formatting your code using the Eclipse code formatter.
L'idée est d'appeler l'API depuis la ligne de commande :
<path-to-eclipse>\eclipse.exe -vm <path-to-vm>\java.exe -application **org.eclipse.jdt.core.JavaCodeFormatter** -verbose -config <path-to-config-file>\org.eclipse.jdt.core.prefs <path-to-your-source-files>\*.java
C'est une approche qui pourrait répondre à nos besoins, mais dans mon cas, je souhaitais pouvoir exécuter depuis un process Java existant.

Avant de pouvoir appeler le formateur JDT, il faut d'abord récupérer les options de formatage. On a deux possibilités : utiliser les préférences du workspace ou utiliser un formateur XML.

Récupération des options depuis les préférences de workspace

Le fichier org.eclipse.jdt.core.prefs contient les préférences de formatage d'un workspace Eclipse.
Il est disponible dans le répertoire .metadata\.plugins\org.eclipse.core.runtime\.settings de votre workspace.
Il propose des options de formatage sous la forme Clé=Valeur. Ce fichier Properties peut donc être lu facilement avec l'API Properties de Java. L'objet Properties implémente Map<Object, Object>, on peut donc récupérer nos propriétés sous forme de Map très facilement avec un simple cast. L'API de formattage de JDT utilisant un Map<String, String>, cette approche nous simplifie le travail.
(je n'indique ici que le code utile...j'ai intentionnellement supprimé les catch et fermeture de flux pour faciliter la lecture)
private static Map<String, String> readPreferences(String filename) {

    File configFile = new File(filename);
    BufferedInputStream  stream = new BufferedInputStream(new FileInputStream(configFile));
    final Properties formatterOptions = new Properties();
    formatterOptions.load(stream);
    return (Map)formatterOptions;
}

Récupération des options depuis un formateur Eclipse

Pour la récupération d'options depuis un fichier de formateur Eclipse, le travail est un peu plus (mais à peine) compliqué.
Le fichier XML n'est qu'une autre représentation du fichier Properties dans laquelle chaque noeud setting propose le couple [clé,valeur] avec les attributs [id, value].
Un parsing du fichier permet donc d'en sortir une Map<String, String> sans trop de difficultés.

private static Map<String, String> readFormatter(String filename) {
    Map<String, String> options = new TreeMap<String, String>();
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);

    DocumentBuilder builder = factory.newDocumentBuilder();
    org.w3c.dom.Document doc = builder.parse(filename);
    XPathFactory xFactory = XPathFactory.newInstance();
    XPath xpath = xFactory.newXPath();
    XPathExpression expr = xpath.compile("//setting");
    Object result = expr.evaluate(doc, XPathConstants.NODESET);

    NodeList nodes = (NodeList) result;
    for(int i = 0; i < nodes.getLength(); i++) {
        Node id = nodes.item(i).getAttributes().getNamedItem("id");
        Node value = nodes.item(i).getAttributes().getNamedItem("value");
        options.put(id.getNodeValue(), value.getNodeValue());
    }
    return options;
}

Formater le code à partir des options

Une fois les options récupérées sous la forme d'un Map<String, String>, il suffit de récupérer un CodeFormatter avec createCodeFormatter(), puis de formater le code à partir du code source existant. Dans notre cas, nous formatons l'intégralité d'un fichier source (K_COMPILATION_UNIT). Le source formatté est récupéré par une gymastique : le TextEdit est appliqué à un Document, le code source est ensuite récupéré depuis le document avec get() sous la forme d'une chaine de caractère.

CodeFormatter formatter = ToolFactory.createCodeFormatter(options);
TextEdit textEdit = formatter.format(CodeFormatter.K_COMPILATION_UNIT, source, 0, source.length(), 0, null);
IDocument document = new Document(source);
textEdit.apply(document);
String formattedSource = document.get();

Conclusion

L'intérêt de pouvoir formater son code de manière programmatique simplifie le travail lors de la création d'un générateur de code. En effet, il n'y a plus besoin de se soucier de prendre en compte le formatage lors de la conception du générateur de code :ce formatage peut peut être fait en post-génération.
L'intérêt de pouvoir paramétrer son formateur et l'externaliser du générateur est autrement plus intéressant si l'on souhaite proposer plusieurs "saveurs de formatage" (idéal notamment pour les Centre de Service en SSII par exemple), car il permet de générer des sources compatibles aux conventions de codage de chaque client de manière très simple et à moindre coût.

les sources complètes sont disponibles sur Github.


mercredi 2 octobre 2013

Valider la syntaxe d'un source Java avec Eclipse JDT

Mettre en place un système de vérification syntaxique de code source est initialement tiré d'un besoin d'avoir un smoke test lors de la génération de code : une première étape permettant de valider à minima que mon code généré est OK. Si je sais que la syntaxe de mon fichier généré n'est pas bonne dès la génération alors il ne me sert à rien de l'intégrer dans mon IDE, ou même d'essayer de le compiler.

La solution est apparu en fouillant JDT pour un autre besoin (formater automatiquement du code d'après un formatter Eclipse...) : l'API de parsing AST de JDT fourni tout le nécessaire pour mon besoin, de manière complète et facile à utiliser.

Récupération des librariries Eclipse JDT

Eclipse met à disposition un ensemble de librairies qui peuvent être utilisées en standalone (j'entend par là, hors du contexte d'exécution d'Eclipse, sans lancer toute l'IHM Eclipse.) et donc tout particulièrement utile dans notre cas.
J'utilise les dernières librairies fournis dans Eclipse Kepler 4.3. Ces librairies ne sont pas disponibles dans les dépôts publics Maven (ou du moins, pas complétement ou de manière assez fouillie...). Je conseille donc de télécharger la dernière version d'Eclipse et de récupérer les librairies suivantes dans le répertoire plugins pour les intégrer à ce projet.
J'ai besoin des librairies suivantes  :
  • org.eclipse.core.contenttype_3.4.200.v20130326-1255.jar
  • org.eclipse.core.jobs_3.5.300.v20130429-1813.jar
  • org.eclipse.core.resources_3.8.101.v20130717-0806.jar
  • org.eclipse.core.runtime_3.9.0.v20130326-1255.jar
  • org.eclipse.equinox.common_3.6.200.v20130402-1505.jar
  • org.eclipse.equinox.preferences_3.5.100.v20130422-1538.jar
  • org.eclipse.jdt.core_3.9.1.v20130905-0837.jar
  • org.eclipse.osgi_3.9.1.v20130814-1242.jar

Comprendre l'API JDT et les problèmes de syntaxe

Le Parser AST d'Eclipse peut s'appuyer directement sur un source en String. Pour cela, il faut quand même le convertir en tableau de char.
  char[] sourceArray = source.toCharArray();
Nous aurions pu également tirer parti de la classe Util (org.eclipse.jdt.internal.compiler.util) qui nous propose de convertir directement le contenu d'un fichier sous la forme d'un tableau de char.
   char[] sourceArray = Util.getFileCharContent(file, encoding);
Nous utilisons le JLS 4 qui permet de parser les sources allant des versions Java 1.1 à 1.7.
Il faut, de plus spécifier une option de compilation pour indiquer le niveau de conformité des sources : en version 1.7 dans notre cas.
Le type de source est également indiqué : Nous pouvons parser une expression (K_EXPRESSION), une déclaration (K_STATEMENT), un corps de classe (K_CLASS_BODY_DECLARATIONS) ou, comme dans notre cas, une classe Java complète (K_COMPILATION_UNIT).
  ASTParser parser = ASTParser.newParser(AST.JLS4);
  Map options = new HashMap(1);
  options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_7);
  parser.setCompilerOptions(options);
  parser.setKind(ASTParser.K_COMPILATION_UNIT);
  parser.setSource(sourceArray);
La source est indiquée au parser grâce à son contenu indiqué dans sourceArray. La méthode createAST() crée un arbre syntaxique que nous pourrions explorer par la suite... mais ce n'est pas l'objet de cet article.
  CompilationUnit unit = (CompilationUnit) parser.createAST(null);
Ce qui nous intéresse ici, c'est de savoir quels sont les problèmes dans notre code source.  Attention à cette étape, seules les erreurs syntaxiques sont remontées (les erreurs de binding, d'imports, etc ne sont pas remonté lors de la remontée dans l'AST). L'analyseur syntaxique fourni par JDT est suffisamment robuste pour nous indiquer toutes les erreurs rencontrées lors du parsing de la classe via la méthode getProblems().
  IProblem[] pbs = unit.getProblems();
Si le tableau est vide, alors de problème de syntaxe. Sinon, les objets IProblem indiquent les portions de code en erreur.
  if(pbs.length > 0) {
      displayProblems(pbs, unit, source, out);
      return false;
   } else {
    return true;
   }
Nous allons donc afficher ces problèmes, pour l'instant en itérant sur chacune des erreurs pour l'afficher, indiquer la ligne de code en erreur et afficher un souligné pour mettre en avant la zone impactée.
La zone en souligné est récupérée depuis le IProblem via les méthodes getSourceStart() et getSourceEnd(). Nous utilisons la méthode utilitaire getColumnNumber() (disponible sur la classe CompilationUnit) pour nous indiquer  l'index de début et de fin par rapport à la ligne courante. Le libellé et le souligné sont affichés par rapport à ces infos.
    
private static void displayProblems(IProblem[] pbs, CompilationUnit unit, String source, PrintStream out) {
    for(IProblem pb : pbs) {
        displayError(pb, out);
        displayLine(source, pb.getSourceLineNumber(), out);
        displayEmphasize(unit.getColumnNumber(pb.getSourceStart()) , unit.getColumnNumber(pb.getSourceEnd()), out);
    }
}

private static void displayError(IProblem pb, PrintStream out) {
    out.append("line ");
    out.append(String.valueOf(pb.getSourceLineNumber()));
    out.append(" : ");
    out.append(pb.getMessage());
    out.append('\n');
}

private static void displayEmphasize(int start, int stop, PrintStream out) {
    int length = stop + 1;
    out.append("  ");
    for(int i=0; i < length; i++) {
        if(i < start || i > stop) {
            out.append(' ');
        } else {
            out.append('-');
        }
    }
    out.append('\n');
}
private static void displayLine(String source, int line, PrintStream out) {
    out.append("  ");
    String[] strlines = source.split("\n");
    out.append( strlines[line - 1 ]);
    out.append('\n');
}

Cas de test sur un exemple concret

J'ai créé un petit cas de tests avec plusieurs erreurs dans le morceaux de code ci-dessous.

package flo.test;
import 1flo.truc.*
import java.util.Date;

   sdf   
public class Toto {
int toto = 123_456;
Bla ex = new Bla();
Truc t ; 
Titi t2; 
private class Titi {}
}

Lors de l'analyse, le morceaux de programme nous remonte les erreurs dans le fichier source et indique les portions qui sont syntaxiquement erronées.

line 2 : Syntax error on token "1f", delete this token
  import 1flo.truc.*
         --
line 2 : Syntax error on token "*", ; expected after this token
  import 1flo.truc.*
                   -
line 5 : Syntax error on token "sdf", delete this token
     sdf   
     ---

Conclusion

L'API Eclipse JDT offre un cadre idéal pour mettre en place rapidement et facilement un système de validation de syntaxe Java (et bien plus encore !). Il n'y a plus qu'à intégrer cette vérification à la suite de notre génération de code pour assurer un premier niveau de validation des sources générées.

 Les sources complètes de la classe sont disponibles sur Github : https://gist.github.com/florentdupont/6799971

Si vous voulez aller plus loin sur l'utilisation de l'AST proposé par Eclipse JDT, je vous conseille l'article de Lars Vogel : Abtract Syntax Tree et Java Model qui explique rapidement les notions de JDT et l'utilisation du Pattern Visitor pour explorer votre arbre. Il regroupe aussi un grand nombre de liens utiles.

jeudi 26 septembre 2013

Suivre le développement des règles métier avec Dolphin et Jenkins

Update : une démo de la page statique est disponible à cette adresse : http://dolphindemo.flo.cloudbees.net/

Dolphin est un plugin Maven d'analyse d'annotations de suivi des exigences métier dans le code Java. Il permet d'assurer la traçabilité de règles métier depus les spécifications détaillées et le code. Il permet de connaitre quelle méthode implémente une règle métier particulière et le niveau d'avancement de celle-ci.

C'est un outil que j'ai développé afin d'assurer la traçabilité et avoir une idée de l'avancement de l'implémentation des règles métier au sein des projets.

Nous allons voir comment le mettre en place sur un projet, puis comment tirer parti de son usine logicielle et de Jenkins pour récupérer les indicateurs de manière automatisée en "quasi" temps réel.
Les sources de Dolphin sont récupérable à cette adresse : https://github.com/florentdupont/dolphin.

Enrichir le code avec l'API

 l'API comporte deux annotations :
  • @BusinessRule, pour la traçabilité des règles métier
  • @DevelopmentStatus, pour le suivi d'avancement.
L'annotation @BusinessRule indique les règles métiers implémentées sur une méthode. Cette annotation porte 2 attributs : 
  • id représente l'identifiant de la règle 
  • version représente la version de la règle. 
Par exemple :
  @BusinessRule(id="RG_0009", version="0.1")
  public void myMethod() {
      // blabla
  }

Si plusieurs règles sont appliquées sur une méthode, alors on peut indiquer les différents identifiants en spécifiant les valeurs multiples entre accolades {} : Dans l'exemple ci dessous, myMethod() implémente la règle RG_0009 en version 0.1 et la règle RG_0010 en version 0.2.
  @BusinessRule(id={"RG_0009", "RG_0010"}, version={"0.1", "0.2"})
  public void myMethod() {
    // ...
  }
L'annotation @DevelopmentStatus permet d'indiquer l'avancement d'un développement. L'annotation porte sur une méthode. Par exemple, une méthode en cours de développement :
  @DevelopmentStatus(StatusType.ONGOING)
  public void myMethod() {
    //...    
  }
Il existe 5 types de statut :

  •  TODO : méthode à implémenter 
  • ONGOING : méthode en cours de développement 
  • DONE : méthode implémentée, mais pas encore testée 
  • TESTED : méthode testée unitairement. Passe les tests unitaires 
  • INTEGRATED : méthode testée en intégration. Passe les tests d'intégration. 
Bien que les deux annotations pourraient être utilisées indépendamment, l'intérêt est évidemment de les utiliser conjointement afin de pouvoir bénéficier de l'avancement de l'implémentation des règles métier. L'exemple ci-dessous montre un exemple d'utilisation conjointe.
  public class MyClass {

    @BusinessRule(id="RG_0001", version="1.0")
    public void myMethod1() {
      // ...
    }

    @BusinessRule(id="RG_0016", version="1.0")
    @DevelopmentStatus(StatusType.DONE)
    public void myMethod2() {
      // ...
    }
}

Analyser les résultats

L'outil fait une analyse statique sur le code compilés (*.class). J'ai implémenté cette analyse en utilisant la librarie ASM qui permet de faire de l'analyse de Bytecode Java. L'utilisation d'une analyse statique depuis le Bytecode est plus complexe à mettre en oeuvre que de l'introspection (quoique...) mais elle présente surtout l'intérêt de pouvoir faire une analyse sans avoir à charger dynamiquement une classe. La classe peut donc très bien ne pas avoir ses classes liées chargées pour être analysée (ce qui est très utile lors de très gros projet et de Maven Dependency Hell...).

Une façon très simple de lancer l'outil, une fois installé, est de lancer la commande suivante : 

  mvn clean package -DskipTests com.dolphin:dolphin-maven-plugin:1.3:dolphin -DdolphinPrefix=com.mypackage.test
Le résultat de l'analyse est disponible dans le répertoire target/dolphin sous la forme d'un rapport HTML.

Le rapport HTML est une application HTML5 statique qui permet d'afficher une échelle du nombre de méthode analysées, ainsi que le status d'avancement de chaque méthode. Le tableau récapitulatif peut être trié par classe, par méthode, par règle ou par version. Il propose également une zone de recherche.
L'export XLS est également possible.

Intégration dans Jenkins 

L'intérêt est de pouvoir automatiser cette analyse et de l'intégrer dans Jenkins pour pouvoir disposer, de manière régulière et en quasi-temps réel, l'avancement.

Etant donné que Dolphin est disponible sous la forme d'un plugin Maven, il est tout à fait possible de l'intégrer à une construction Jenkins.

Voici un type de paramétrage pouvant être mis en place : 
Ce qui déclenche le Build : 
Scrutation de l'outil de gestion de version : H/20 * * * 1-5 
Build : 
Goals et options : 
  mvn clean package -DskipTests com.dolphin:dolphin-maven-plugin:1.3:dolphin -DdolphinPrefix=com.mypackage.test
Action à la suite de Build :
Archiver des artefacts : fichiers à archiver :
target/dolphin/**

C'est cette dernière étape qui va nous intéresser. Nous utilisons Jenkins comme "serveur Web", en mettant à disposition les fichiers HTML statiques directement accessible depuis ses constructions.
En cliquant sur index.html, on se rend compte que le résultat est accessible sur une URL du type :
http://MY_SERVER/jenkins/job/MY_JOB/BUILD_NUMBER/artifact/target/dolphin/index.html
On peut donc facilement, en gérant la rétention des constructions, garder une version de l'export par construction et ainsi garder un historique des différentes analyses Dolphin.

 L'intérêt pour le chef de projet est d'avoir une vision de la dernière analyse. Dans ce cas, on tire parti des URL Jenkins pour nous aider puisque Jenkins met à disposition les dernière construction sous un formalisme d'URL toujours identique, à savoir :;
http://MY_SERVER/jenkins/job/MY_JOB/lastSuccessfulBuild/artifact/target/dolphin/index.html
Ce lien peut ensuite être utilisé pour accéder rapidement aux résultats de dernière analyse, peu importe le numéro de construction utilisé par Jenkins, et notamment être utilisé directement depuis un tableau de bord par exemple.

samedi 21 septembre 2013

Jenkins & SVN : Afficher la révision comme numéro de build

Le numéro de build indiqué par défaut lors de la construction dans Jenkins est un numéro qui s'incrémente à chaque construction. Ce numéro - mis à part de nous indiquer à combien de construction nous sommes rendu - n'apporte pas plus d'information.


Ce qui serait utile, par contre, c'est de savoir rapidement à quelle version de source cette construction correspond afin de disposer d'une meilleure traçabilité entre Source SVN -> artefact Jenkins.
Aucun plugin ne permet de le faire actuellement, nous allons voir comment on peut cependant le mettre en place rapidement.

Retour sur les versions de SVN

Dans notre contexte, nous avons plusieurs projets disponibles sur un repository SVN. Chaque projet est donc sur un sous-répertoire depuis la racine du dépot. A chaque commit (peu importe sur quel projet), le numéro de révision du dépôt est incrémenté.
Ce numéro de révision est global : il permet de restituer l'état d'un repository à un révision donnée.
Pour chaque sous répertoire, nous disposons d'un second indicateur : Last Changed Revision qui indique le dernier numéro de révision qui à modifié le répertoire en cours

Nous pouvons récupérer ces informations depuis la ligne de commande en se positionnant sur un répertoire de source précédemment checkoutée :

$ svn info
Path: .
Working Copy Root Path: D:\projets\toto\source\
URL: http:/xxx/usvn/svn/toto/trunk
Repository Root: http://xxx/usvn/svn/toto
Repository UUID: a1ebbc07-6c76-4030-a489-aa0d2fd72fa2
Revision: 11372
Node Kind: directory
Schedule: normal
Last Changed Author: fdupont
Last Changed Rev: 11360
Last Changed Date: 2013-09-11 17:23:54 +0200 (mer., 11 sept. 2013)

Les informations qui nous intéressent apparaissent dans les champs "Revision" et "Last Changed Rev".

Prise en compte dans Jenkins

Pour la prise en compte dans Jenkins, il faudra ajouter le plugin Hudson Groovy builder

Dans la configuration du build, ajouter une Pre steps avec execution de script Groovy "System". Exécuter en tant que script "System" signifie que le script sera exécuté dans la même JVM que Jenkins, ce qui permet d'avoir la main sur les objets Jenkins. C'est ce qui va nous permettre de modifier le nom du build ainsi que sa description.

Les deux lignes suivantes sont les imports des packages jenkins.
  import hudson.model.*
  import hudson.util.*
Récupère l'instance du build courant
  def build = Thread.currentThread().executable
On récupère ensuite le workspace du build courant, c'est à dire le répertoire dans lequel les sources sont chéckoutée. Ce répertoire contient le répertoire .svn qui sera utilisé par la commande svn info.
  def workspace = build.getWorkspace()
En groovy, le moyen le plus simple d'exécuter une commande shell et de récupérer le résultat est de passer par une tâche Ant.
  def ant = new AntBuilder()

  ant.exec(executable: "svn", outputproperty: "output", dir: workspace){
    arg(line: "info")

  }
La sortie est récupérée depuis la variable "outputProperty" puis sert à alimenter la variable svnInfo.
  svnInfo = ant.project.getProperty("output")
On utilise ensuite des expression régulière pour récupérer le numéro de révision ainsi que le LCR (Last Changed Revision.
  def pattern = /Last\s+Changed\s+Rev:\s+(\d+)/
  def matcher = (svnInfo =~ pattern)
  def lcr = "Last changed revivion : <b>" + matcher[0][1] + "</b>"
  
  pattern = /Revision:\s+(\d+)/
  matcher = (svnInfo =~ pattern)
  def rev = "r" + matcher[0][1]
Le nom du build est ensuite modifié avec l'intitulé de la révision et la description est renseignée avec le LCR.
  build.setDisplayName(rev)
  build.setDescription(lcr)

Au complet, ça nous donne :
  import hudson.model.* 
  import hudson.util.*

  def build = Thread.currentThread().executable
  def workspace = build.getWorkspace()
  def ant = new AntBuilder() 

  ant.exec(executable: "svn", outputproperty: "output", dir: workspace){ 
    arg(line: "info") 
  }

  svnInfo = ant.project.getProperty("output")
  def pattern = /Last\s+Changed\s+Rev:\s+(\d+)/ 
  def matcher = (svnInfo =~ pattern)
  def lcr = "Last changed revivion : " + matcher[0][1] + ""

  pattern = /Revision:\s+(\d+)/ 
  matcher = (svnInfo =~ pattern)
  def rev = "r" + matcher[0][1]

  build.setDisplayName(rev)
  build.setDescription(lcr)


Lors de la prochaine construction, depuis la View, on verra bien apparaitre notre numéro de révision


Dans la vue Constructions, le LCR apparait en tant que description :




Note complémentaire : pour les ceux qui utilisent SVN depuis Windows, l'outil en ligne de commande est internationalisé et répondra donc en français. Pour restituer les sorties consoles en anglais, ajouter la variable d'environnement suivante :
  set LC_MESSAGES=en_US
L'article original : http://jayflowers.com/WordPress/?p=258

vendredi 12 avril 2013

Les 10 commandements d'un développeur humble

Les 10 commandements d'un développeur humble,  (en anglais, the 10 commandments of egoless programming) sont apparus dans le livre de Gerald Weinberg : "The Psychology of Computer programming" dont la publication originale date de 1971, mais restent toujours d'actualité !

I. Comprend et accepte que tu feras des erreurs.

II. Tu n'es pas ton code

III. Peu importe combien de Karaté tu connais, quelqu'un d'autre en connaitra toujours plus

IV. Ne ré-écrit jamais le code d'un autre sans consultation

V. Traite les gens qui en connaissent moins que toi avec respect, égard et patience

VI. La seule constante dans le monde est le changement

VII. La seule autorité provient du savoir, pas de la position

VIII. Bat toi pour ce que tu crois, mais accepte la défaite avec dignité.

IX. Ne soit pas "le mec dans son coin"

X. Critique le code et pas les personnes. Soit respectueux envers les personnes, pas envers le code.


A méditer au quotidien....

lundi 28 janvier 2013

5 Bonnes raisons du MDA pour le développement mobile

 
Article co-écrit par Florent Dupont et Lamia Gaouar, qui prépare actuellement un doctorat SIC autour des développements Android et MDA à l'université de Tlemcen.

La démocratisation des plateformes nomades (mobiles et tablettes) poussent les entreprises à proposer toujours plus de services aux utilisateurs. Elles ont, en effet, bien compris tous les enjeux qui se trament derrière les applications mobile : communication améliorée, nouveaux services aux utilisateurs, régie publicitaire, avantage concurrentiel, etc.

Cet engouement pour les plateformes nomades offre de nouvelles perspectives de marché, notamment pour les entreprises éditrices (que ce soient des Mobile Agencies, des Web Agencies ou encore des SSII) qui peuvent proposer leur expertise de développement. Depuis 2011, le marché des ventes de mobile dépasse les ventes de PC et offre donc des perspectives de marché de développement d’applications mobile très importantes.

Ces éditeurs doivent cependant s’adapter aux plateformes de développement mobile qui sont foncièrement différentes de celle des applications Desktop. En effet, ces plateformes imposent des contraintes fortes, les ressources étant limitées : taille d’écran réduite, processeur faible, stockage limité, connexion lente et intermittente, RAM limitée. Toutes ces limitations impliquent une discipline de développement particulière pour que l’expérience utilisateur soit toujours efficace.

Au niveau logiciel, de multiples OS (Android, iOS, BlackBerry, Windows Phone) se disputent le marché de la mobilité. A chaque OS correspond une plateforme de téléchargement d’applications, des outils de développement et un langage de programmation permettant le développement et la distribution des applications :

 


Cette hétérogénéité des outils de développement et des langages, rend difficile le développement d’applications mobile multiplateformes. Elle pousse les développeurs à faire un choix sur la plateforme, tout en assurant la plus grande distribution possible.

Des outils pour aider les développeurs ?

Google a récemment apporté un élément de réponse avec le projet J2ObjC qui permet de convertir du code Java (Android) en code Objective-C (iOS). L’intérêt d’une telle démarche est de pouvoir récupérer le code fonctionnel d’une application Android afin de faciliter la conversion vers la plateforme iOS.
Mais cette approche pose encore de nombreuses questions :
  • Qu’en est-il de la conversion Objective-C vers Java ? Et vers d’autres OS  ?
  • Et plus généralement, comment capitaliser sur le fonctionnel d’une application indépendamment des préoccupations techniques afin d’en faciliter la migration ?  

Une réponse avec le MDA

L’approche MDA a su faire ses preuves pour le développement d’applications d’entreprise et peut également apporter beaucoup pour les applications mobile. Au travers de 5 points, nous allons voir pourquoi et comment l’approche MDA peut nous aider à assurer la pérennité des savoir-faire, le gain de productivité tout en répondant aux problématiques de fragmentation des plateformes mobile.

1. Réduire les problématiques de fragmentation

Chaque OS mobile détient une part du marché, et donc chacun possède sa communauté d’utilisateurs et de développeurs. Cette fragmentation pose un souci pour les entreprises désireuses de toucher un large public car le développement des applications multiplateformes est très coûteux en temps et en argent. Les entreprises se trouvent face à un dilemme :
  • Développer une application de qualité en un temps efficace et limiter sa distribution à une plateforme spécifique. 
  • Développer une application aux fonctionnalités réduites, mais proposée sur plusieurs plateformes et à un large panel d'utilisateurs.
Des solutions hybrides existent comme Cordova (application native construite à partir de vue Web et Html5) ou Flex 4.6 (exécution sous plateforme Flash) et permettent d’assurer du multiplateformes, mais leur prise en charge reste techniquement partielle. En réalité, une application native restera plus adaptée pour plusieurs raisons :
  • L’utilisateur dispose d’un environnement adapté à sa plateforme et à ses spécificités : l’expérience utilisateur reste optimale.
  • Le développeur tire profit de l’architecture spécifique et optimise l’application en fonction de la plateforme.
  • L’application peut évoluer et éventuellement tirer parti des fonctionnalités spécifiques à une plateforme ou OS.

L’approche MDA peut répondre à ces problématiques : décrire les besoins fonctionnels d’une application indépendamment de la plateforme d’exécution, et respecter les spécificités techniques de chaque plateforme. MDA permet de générer le code sans valeur ajoutée (boilerplate code) spécifique à chaque plateforme. On assure ainsi un gain de productivité, même sur des applications natives.

2. Capitaliser sur les concepts proches

Chaque OS mobile utilise son propre langage de programmation et arbore ses propres APIs mais les concepts généraux restent standards et communs. On peut se permettre de capitaliser des concepts communs entre différentes plateformes permettant de modéliser une application de manière générique, citons par exemple :
  • Activité : correspond à une page de l’application, elle est une notion commune à tous les OS. Une application est composée d’une ou de plusieurs activités.
  • Gestion des données de l’application : en séparant le code de l’interface graphique et le code fonctionnel.
  • Vues et actions associées : une vue est un objet graphique par lequel l’utilisateur interagit avec l’application ce qui provoque une action liée à la vue. Nous retrouvons la plupart des vues sur tous les OS mobile : bouton, saisie de texte, label, barre de navigation, bouton radio, case à cocher, etc.
  • Type d’évènements : une application doit prendre en charge certains évènements susceptibles d’apparaitre durant son exécution tels que les appels téléphoniques, les SMS, etc.
  • Applications natives : les applications natives telles que le calendrier, les contacts, les alarmes, le lecteur média sont communs à tous les OS.
L’approche MDA divise la conception de l’application en deux points de vue :
  • Un point de vue "fonctionnel" indépendant des détails liés à la plateforme d’exécution. On définit ce que l’on veut générer (le quoi). Dans cette partie, on modélise des notions abstraites telles que des écrans, boutons, objets métier, DAO, mais en restant indépendant de leur implémentation technique. Modéliser le fonctionnel de l’application assure la pérennité des savoir-faire.
  • Un point de vue "technique" dans lequel sera décrit l’architecture de la plateforme d’exécution. On définit comment on veut le générer (le comment). Dans cette partie, on spécifie comment la notion fonctionnelle doit être générée : quel langage (Objective C, Java), quelle version (Android 4.1 par exemple), quelle implémentation de BDD. Modéliser l’architecture technique assure la prise en compte de la plateforme d’exécution.
L’étape qui suit la description fonctionnelle et technique d’une application est la génération du code source, c’est le rôle du générateur (ex. MIA Studio, Acceleo). Il s’appuie sur le modèle fonctionnel et technique de l’application pour générer du code source que le développeur pourra enrichir manuellement (le traitement algorithmique et les ressources qui ne peuvent pas être déduits du modèle). La modélisation nous assure une pérennisation du savoir fonctionnel. La génération de code assure un gain de temps considérable entrainant un gain de productivité.

3. Elargir l’écosystème mobile

L’écosystème - tout ce qui vit autour d’une plateforme pour en faciliter le développement - existant sous JavaEE/.NET est large, mais sur plateforme mobile, cet écosystème est encore réduit. L’utilisation de librairies tierces dans les applications mobile est limitée par les différents OS pour plusieurs raisons :
  • Une taille d’application importante : l’utilisation massive de librairies est au contraire des bonnes pratiques pour le développement mobile (problématique d’espace mémoire).
  • Corrolaire du point précédent : une taille d’application trop importante peut être un frein à son téléchargement (debit limité et frais des réseaux 3G).
  • Le manque de visibilité des développements des librairies. Alors que les librairies pour applications JEE/.Net sont portées par de grands éditeurs (JBoss, Spring, Microsoft, etc.), les librairies mobile existantes sont quasi-exclusivement développées par des développeurs tiers. Le suivi et la maintenance ne sont donc pas toujours assurés.
  • Les éditeurs Google, Apple, Microsoft gardent la main sur les évolutions des SDK et ne garantissent donc pas la compatibilité des librairies tierces avec les nouvelles versions des plateformes.
Pour assurer la pérennité du code et élargir son périmètre de compatibilité à plusieurs versions, il est donc préférable d’être indépendant au maximum des librairies tierces et d’avoir un code standard au SDK. L'approche MDA réduit le temps de développement en générant du code standard au SDK, en respectant les bonnes pratiques d’architecture et de qualité.

4. Industrialiser le développement

La mise en place d’une approche MDA, conjointement avec les différents composants d’une usine logicielle (construction, Intégration continue, analyse qualimétrique), industrialise le développement et assure une bonne qualité au code.
L’industrialisation avec le MDA nécessite plusieurs éléments clés :
  • La mise en place d’un POC par une équipe d’architectes logiciels. Ce POC servira de base de code attendu pour faciliter la réalisation du générateur.
  • Un code généré ne doit pas être source d’erreurs et doit rester compatible avec les niveaux de qualimétrie logicielle (qualité, architecture).
  • Une formation des développeurs à l’utilisation du DSL et du générateur.
  • Un support technique permettant d’accompagner les développeurs sur les outils afin d’assurer un bon démarrage du projet.
  • Une communauté autour du générateur qui remonte les bugs. L’amélioration en continue du générateur profite aux développements en cours et aux prochains projets.
  • Une capitalisation sur les bonnes pratiques de développement afin de développer des futures applications encore plus rapidement.
Sur le marché, il y a encore peu de développeurs avec des compétences “mobile”. L’industrialisation des développements mobile par l’approche MDA, a encore plus de sens dans ce contexte, car il permet d’avoir un meilleur accompagnement et un meilleur suivi.

5. Réduire le coût de possession

Avec des budgets toujours plus serrés, les entreprises veulent réduire le coût de possession de leurs applications. L’approche MDA, et plus spécifiquement le DSL et le générateur de code ont une grande valeur de capitalisation et de réutilisabilité. Elles permettent ainsi de réduire les coûts de possession, notamment sur l’investissement initial et le coût de maintenance  :
  • Une conception facilitée par un DSL adapté, cadrant les besoins spécifiques aux plateformes mobile. 
  • Un générateur de code disponible “sur étagère” et utilisable rapidement permet de booster le démarrage d’un projet
  • Réduction du support technique : le code ou les patterns complexes et qui sont enclins aux erreurs (typiquement liés à la persistance, mapping ORM) sont générés.
  • Un code de qualité suivant les bonnes pratiques assure une meilleure maîtrise et une réduction des coûts de maintenance
  • Un processus itératif : l’outillage MDA par amélioration continue va réduire les coûts des projets progressivement au fur et à mesure de son amélioration.
Un OS peut aujourd'hui être leader du marché et demain perdre certains marchés. Un exemple, avec les annonces de Samsung, premier fabricant mondial de smartphones, qui compte  sortir de nouveaux smartphones en 2013 en abandonnant Android pour Tizen. Ou encore à l’image du géant chinois Huawei qui propose son propre OS mobile ou encore plus récemment Firefox avec FirefoxOS.



En conclusion, au lieu d'investir sur la technologie qui abrite une application, mieux vaut investir dans une technique de développement qui permet de capitaliser la fonctionnalité d'une application indépendamment de sa technologie. Une méthode qui intègre l'évolutivité et la pérénité dans son processus de développement comme l'approche MDA.