Qu'est-ce que Maven ?

Maven est une sorte de super-gestionnaire de projet qui peut se charger d'à peu près tout : dépendances, compilation, packaging, lancement des tests unitaires, ... L'outil est disponible sur la plupart des distribution, pour ma part sous Ubuntu :

$ sudo aptitude install maven2

L'objectif de ce billet est d'expliquer comment, à l'aide de notre génial ingénieur de recherche, j'ai pu utilisé maven pour gérer la construction du collection reader pour Wikipedia.

Récupérer les dépendances

La première complication lorsque l'on souhaite compiler le collection reader ce sont les dépendances, il y en a trois :

Par chance tous ces projets sont déjà gérés par maven ce qui va fortement nous faciliter la tâche. Les étapes à suivre sont les suivantes :

  1. Obtenir une version des sources de chacun des projets
  2. Les compiler avec maven
  3. Les installer dans le dépôt local de maven

Ainsi pour mwdumper :

$ svn co http://svn.wikimedia.org/svnroot/mediawiki/trunk/mwdumper
...
$ cd mwdumper
$ mvn compile
...
$ mvn install
...

Une fois ces étapes terminées, un nouveau dossier doit apparaître dans votre dépôt local : ~/.m2/repository/org/wikimedia/mwdumper/ ; il doit contenir un dossier correspondant à la version compilée (1.16 pour moi) et dans ce dossier les fichiers suivants :

  • mwdumper-1.16.jar
  • mwdumper-1.16.pom

Il faut procéder de la même manière pour WikiModel :

$ svn co http://wikimodel.googlecode.com/svn/trunk/org.wikimodel.wem
...
$ cd org.wikimodel.wem
$ mvn compile
...
$ mvn install
...

Vous devriez de la même manière voir apparaître dans votre dépôt local : ~/.m2/repository/org/wikimodel/org.wikimodel.wem/ ; il doit contenir une structure similaire.

Ces étapes sont nécessaires pour la suite car elles placent les dépendances dans le dépôt local de maven, là où il ira les chercher lors de la compilation.

Écriture du pom.xml

Revenons maintenant au composant UIMA. L'intelligence de Maven se configure dans un fichier à la racine du projet et nommé pom.xml.

Dans un premier temps, il faut définir le projet en lui donnant :

  • Un identifiant de groupe (dans le cas où le projet appartiendrait à un groupe de projets) : groupId
  • Un identifiant d'artefact, c-à-d de ce que le projet va produire : artifactId
  • Spécifier une version : version
  • Donner un nom et une description
  • Renseigner les informations concernant les licences
  • Spécifier le type de packaging que l'on souhaite obtenir
  1. <project
  2. xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
  5. >
  6. <modelVersion>4.0.0</modelVersion>
  7. <groupId>uima.wikipedia</groupId>
  8. <artifactId>uima-mediawiki-loader</artifactId>
  9. <version>0.4</version>
  10. <packaging>jar</packaging>
  11. <name>MediaWiki UIMA Loader</name>
  12. <description>This is a UIMA Collection Reader for the MediaWiki dumps (Wikipedia &amp; co).</description>
  13. <licenses>
  14. <license>
  15. <name>Apache 2</name>
  16. <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
  17. </license>
  18. </licenses>
  19. ...
  20. </project>

Dans un second temps, nous définissons les dépendances nécessaires à la construction de notre composant. Comme nous l'avons vu précédemment, il y en a plusieurs :

  • UIMA, et plus précisément uimaj-core et uimaj-document-annotation
  • WikiModel, et plus précisément org.wikimodel.wem
  • MWdumper

Les dépendances se déclarent entre les tags <dependencies>, en indiquant notamment la version nécessaire.

  1. ...
  2. <dependencies>
  3. <!-- Required to generate the Java classes -->
  4. <dependency>
  5. <groupId>org.apache.uima</groupId>
  6. <artifactId>uimaj-core</artifactId>
  7. <version>2.3.0-incubating</version>
  8. <scope>compile</scope>
  9. </dependency>
  10. <!-- DocumentAnnotation UIMA Type -->
  11. <dependency>
  12. <groupId>org.apache.uima</groupId>
  13. <artifactId>uimaj-document-annotation</artifactId>
  14. <version>2.3.0-incubating</version>
  15. <scope>compile</scope>
  16. </dependency>
  17. <!-- WikiModel -->
  18. <dependency>
  19. <groupId>org.wikimodel</groupId>
  20. <artifactId>org.wikimodel.wem</artifactId>
  21. <version>2.0.7-SNAPSHOT</version>
  22. </dependency>
  23. <!-- WikiMedia Dumper -->
  24. <dependency>
  25. <groupId>org.wikimedia</groupId>
  26. <artifactId>mwdumper</artifactId>
  27. <version>1.16</version>
  28. </dependency>
  29. </dependencies>
  30. ...

Comme vous l'avez certainement remarqué, on a précisé que le projet dépendait d'UIMA, mais contrairement aux dépendances sur MWDumper et WikiModel, nous n'avons pas installé ces dernières dans le dépôt maven local. En fait Maven va être capable d'aller chercher tout seul ces dépendances grâce au dépôt maven mis en ligne par Apache UIMA. Il suffit de préciser l'existence de ce dépôt :

  1. ...
  2. <repositories>
  3. <!-- Apache UIMA repository -->
  4. <repository>
  5. <id>apache</id>
  6. <name>Apache</name>
  7. <url>http://people.apache.org/repo/m2-incubating-repository</url>
  8. </repository>
  9. </repositories>
  10. ...

Finalement, on précise le processus de construction du composant entre les tags <build>. Dans nos cas cela revient tout simplement à préciser où aller chercher les sources, où placer les fichiers compilés et préciser ce qui doit être considérés comme des ressources et donc placé dans le Jar en plus des classes.

On utilisera de plus le plugin maven-compiler-plugin afin de préciser la version de Java que l'on souhaite pour la compilation.

  1. ...
  2. <build>
  3. <plugins>
  4. <!-- Java Compiler -->
  5. <plugin>
  6. <groupId>org.apache.maven.plugins</groupId>
  7. <artifactId>maven-compiler-plugin</artifactId>
  8. <version>2.0.2</version>
  9. <configuration>
  10. <source>1.5</source>
  11. <target>1.5</target>
  12. </configuration>
  13. </plugin>
  14. </plugins>
  15. <sourceDirectory>src</sourceDirectory>
  16. <outputDirectory>bin</outputDirectory>
  17. <resources>
  18. <resource>
  19. <directory>desc</directory>
  20. </resource>
  21. </resources>
  22. </build>
  23. ...

Le pom.xml est suffisant à cette étape pour permettre de lancer la compilation du projet et le packaging.

$ mvn compile
...

Vous devriez constater que le dossier bin s'est peuplé des classes compilées.

$ mvn package
...

Vous devriez maintenant constater l'apparition d'un dossier target dans lequel vous trouverez notamment un Jar nommé : uima-mediawiki-loader-0.4.jar. Et voilà, dans l'état il est possible d'obtenir un Jar du projet à partir des sources sans trop de problèmes. Toutefois la première étape de récupération des dépendances me paraît trop contraignantes. Il est possible, à l'instar d'Apache UIMA, de mettre en place un dépôt contenant des versions compilées des dépendances afin que maven aille directement les chercher.

Mettre en place un dépôt pour les dépendances

Un dépôt maven ce n'est ni plus ni moins qu'un système de fichiers respectant une certaine structure et accessible par http (par exemple).

J'ai créé un dossier sur mon serveur que j'ai rendu accessible par http à l'aide d'Apache, puis j'y ai collé l'arborescence concernant mwdumper et wikimodel quiu était présente dans mon dépôt local :

  • ~/.m2/repository//org/wikimedia/...
  • ~/.m2/repository//org/wikimodel/...

Il y a une petite nuance tout de même, lorsque le dépôt est distant, les fichiers doivent être accompagnés de leurs checksums afin de vérifier que le téléchargement s'est bien déroulé. Le plus simple pour générer ces fichiers de checksums est de réitérer l'installation dans le dépôt maven local avec une option supplémentaire :

$ mvn install -DcreateChecksum=true
...

Vous trouverez alors dans le dépôt local les fichiers en *.md5 et *.sha1 qu'il faut également transférer sur le serveur.

Une fois que le dépôt distant est mis en place, il suffit de le déclarer dans le pom.xml :

  1. ...
  2. <repository>
  3. <id>uima-fr.org</id>
  4. <name>UIMA Fr</name>
  5. <url>http://www.uima-fr.org/m2-repo/</url>
  6. </repository>
  7. ...

Il est maintenant possible de compiler le composant sans avoir à récupérer les dépendances en amont. Tout se fait automatiquement et de manière transparente pour l'utilisateur... c'est assez plaisant.

Comme je suis un fainéant, je trouve que ce serait cool de pouvoir déployer automatiquement mon composant sur mon dépôt, c'est tellement pratique les dépôts !

Déployer le composant sur le dépôt

J'ai choisi de pouvoir déployer mon composant sur le dépôt par ssh, pour des questions de sécurité. Mais il est également possible de le faire par ftp. Le déploiement par ssh nécessite tout d'abord de pouvoir se connecter automatiquement au serveur par ssh à l'aide d'un échange de clé, puis il suffit de renseigner dans le pom.xml l'adresse du dépôt et dans ~/.m2/settings.xml de préciser les modalités de connexion au dépôt.

Connexion automatique par ssh

Je ne vais pas détailler ici comment déployer un serveur ssh et faire tourner un ssh-agent en local, il y a tout un tas de tutoriels disponibles sur internet pour ça. Ce qu'il faut juste retenir, c'est que maven ne se connectera au serveur ssh si les deux conditions suivantes sont remplies :

  • Le serveur est connu de ssh (il est enregistré dans .ssh/know_hosts) ;
  • La connexion peut se faire par échange de clés (pas de mot de passe).

Le plus simple pour s'assurer de tout cela est de se connecter directement manuellement au serveur :

  • Si le serveur est inconnu, ssh demandera s'il doit être ajouté à la liste des hôtes connus : acceptez ;
  • Si le serveur vous demande un mot de passe c'est qu'il ne connaît pas votre clé, il suffit de lui donner.

Copiez donc le contenu de votre clé publique que vous trouverez dans le fichier ~/.ssh/id_dsa.pub ou bien ~/.ssh/id_rsa.pub. Elle doit ressembler à quelque chose comme ceci :

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzqedhFIi8hy743U7pEvLMQvCEeAo/CxmLjF4jF2WguguN+U/4GsJrONvgoWMYXRn0zVMoHNpCEXQ+BT80ZTnv+MILu5elgFsE18bFA+7qjd454LwuZpoIoJOsCNyJKyGjy7ER5cZGN/z8G6cmSJTGauc270W7WJQELqKM3rfqPJH4FXPF9+WDP4UK/o7k54g36/3hHeBmqW++mpyEwkm0eT+GlBRlmP4NjVJACMoyYwl2S1Ep/m85aYR+95m3neHFZpUPmEyN52/Sod7ak28AHZ0M5oE/nRoUr1AAc0LzJw7BM327fAO6o7iHcfoIdo7pix2KLoteqT8tQIRQUmzxQ== grdscarabe@grdscarabe-desktop

Attention, cette clé se trouve sur le poste client à partir duquel maven se connaîtra !

Copiez donc cette clé dans le fichier ~/.ssh/authorized_keys, si ce fichier n'existe pas créez-le. Faites bien attention à ce qu'il soit dans le répertoire personnel de l'utilisateur que maven utilisera pour se connecter (le votre très certainement).

Une fois cette opération effectuée, vous devriez pouvoir vous connecter au serveur sans que ce dernier ne vous demande de mot de passe. Il se peut que si vous ayez protégé votre clé privée par un mot de passe, le ssh-agent vous le demande. Dans ce cas vous êtes de mon point de vue suffisamment au courant du problème pour ne pas lire cette section. Sinon vous êtes bien malin d'avoir mis un mot de passe :)

Déclaration du dépôt dans le pom.xml

Il suffit de déclarer dans le dépôt dans le pom.xml, nous l'appellerons ici uimafr-repository. Il est également nécessaire de charger l'extension wagon-ssh-external qui permet à maven de déployer par ssh :

  1. ...
  2. <extensions>
  3. <!-- Enabling the use of SSH -->
  4. <extension>
  5. <groupId>org.apache.maven.wagon</groupId>
  6. <artifactId>wagon-ssh-external</artifactId>
  7. <version>1.0-beta-6</version>
  8. </extension>
  9. </extensions>
  10. <distributionManagement>
  11. <repository>
  12. <id>uimafr-repository</id>
  13. <url>scpexe://www.uima-fr.org//home/www-data/org_uima-fr_www/m2-repo</url>
  14. </repository>
  15. </distributionManagement>
  16. ...

Modalités de connexion au dépôt

Enfin, il faut localement configurer maven pour reconnaître le dépôt uimafr-repository. Cette configuration se fait par le fichier ~/.m2/settings.xml. S'il n'existe pas, créez-le puis copiez-collez y le contenu suivant :

  1. <settings>
  2. <servers>
  3. <server>
  4. <id>uimafr-repository</id>
  5. <configuration>
  6. <sshExecutable>ssh</sshExecutable>
  7. <scpExecutable>scp</scpExecutable>
  8. </configuration>
  9. </server>
  10. </servers>
  11. </settings>

En gros ce dernier permet de préciser quels sont les outils à utiliser pour la connexion ssh. Sous linux, nous utiliserons les outils ssh pour la connexion et scp pour le transfert de fichier.

Déploiement

Et voilà, maintenant pour déployer mon composant sur le dépôt, il me suffit de faire :

$ mvn deploy
...

Elle est pas belle la vie ?

Extensions possibles

Il y a bien des extensions possibles pour rendre maven encore plus pratique pour la gestion de ce projet. J'en vois notamment deux :

  • La génération automatique des classes de types UIMA à partir de JCasGen ;
  • L'empaquetage dans le même Jar du projet et des dépendances sur MWDumper et WikiModel.

Dans le premier cas, la solution au problème doit certainement se trouver du côté du ''exec-maven-plugin''. Dans le second cas, il faudrait aller voir du côté de ''maven-assembly-plugin''.

Nouvelle version du composant

Et je conclus ce looong billet en distribuant la nouvelle version, estampillée 0.4, du collection reader pour Wikipedia : par ici ! Vous trouverez les dépendances nécessaires à son fonctionnement dans le dépôt maven de uima-fr.

Vous pouvez tester le composant à l'aide du cpeGui :

$ UIMA_CLASSPATH=~/.m2/repository/uima/wikipedia/mwuima-loader/0.4/mwuima-loader-0.4.jar:~/.m2/repository/org/wikimedia/mwdumper/1.16/mwdumper-1.16.jar:~/.m2/repository/org/wikimodel/org.wikimodel.wem/2.0.7-SNAPSHOT/org.wikimodel.wem-2.0.7-SNAPSHOT.jar cpeGui

Bien sûr, si vous n'avez pas installé le composant avec maven, il faudra modifier en conséquence les chemins du UIMA_CLASSPATH.

Autres articles