Tutorial pour développer un add-on sur Atlassian JIRA - les outils du développeur

Add-on JIRA Pour développer un add-on sur le produit Atlassian JIRA, la mise en place d’un environnement de développement confortable et productif peut être long. Une bonne connaissance sur Java et surtout maven peut être un pré-requis indispensable même si la documentation JIRA en anglais est relativement complète. L’objectif de ce billet est de présenter certains problèmes rencontrés et de donner les solutions.

Pour commencer un plugin, il est recommandé de compiler et d’exécuter un premier tutorial. Cela permet de vérifier que l’environnement de développement est bien configuré sur un add-on de référence fournit par Atlassian.

Attention JIRA en très gourmand, prévoir :

  • 8 GO de RAM sinon ça risque d’être très lent
  • 200 MO de disque pour télécharger les très nombreuses dépendances JIRA
  • un disque SSD car il y a beaucoup de copies de jar dans le cycle de vie maven.

A la découverte du SDK JIRA

Pour installer le SDK JIRA, on peut lire la documentation officielle

Ce SDK encapsule le binaire maven en incluant un repository maven avec quelques dépendances. Le repository maven local du SDK est incomplet, quelques centaines de MO et 30 - 60 minutes de téléchargement sont à prévoir.

Le SDK JIRA encapsule les commandes maven standard (atlas-clean pour mvn clean, atlas-run pour mvn tomcat7:run par exemple), on peut oublier temporairement la commande “mvn” même si les notions qui sont derrière sont les mêmes.

Repository maven et settings.xml local

Pour commencer, explorer le SDK JIRA est un bon début pour comprendre ce qu’il contient.

Il faudra certainement quelques configurations en plus par rapport à la documentation officielle si vous êtes dans un contexte “entreprise”. Sur le poste d’un développeur java, maven est souvent déjà installé avec un settings.xml propre à la machine du développeur. Par exemple, celui-ci contient des informations sur le proxy d’entreprise.

Après avoir installé le SDK, la commande suivante permet de connaître la configuration réellement utilisée :

atlas-version
    
ATLAS Version:    5.0.13
ATLAS Home:       /usr/share/atlassian-plugin-sdk-5.0.13
ATLAS Scripts:    /usr/share/atlassian-plugin-sdk-5.0.13/bin
ATLAS Maven Home: /usr/share/atlassian-plugin-sdk-5.0.13/apache-maven-3.2.1
Executing: /usr/share/atlassian-plugin-sdk-5.0.13/apache-maven-3.2.1/bin/mvn --version -gs /usr/share/atlassian-plugin-sdk-5.0.13/apache-maven-3.2.1/conf/settings.xml
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0
Apache Maven 3.2.1 (ea8b2b07643dbb1b84b6d16e1f08391b666bc1e9; 2014-02-14T18:37:52+01:00)
Maven home: /usr/share/atlassian-plugin-sdk-5.0.13/apache-maven-3.2.1
Java version: 1.8.0_25, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre
Default locale: fr_FR, platform encoding: UTF-8
OS name: "mac os x", version: "10.10.3", arch: "x86_64", family: "mac"

Si rien n’a été changé, il utilise le binaire maven et le settings.xml présent dans le SDK. Cette configuration ne sera certainement pas suffisante dans un contexte “entreprise” car une configuration spécifique est nécessaire pour télécharger les dépendances maven du fait des règles de sécurité. On peut donc préciser le settings.xml de la façon suivante :

atlas-version -gs $TONHOME/.m2/settings.xml

Il faudra dans ce cas ajouter les options présentes dans le settings.xml du SDK en renseignant les informations liées aux repositories maven. Pour ne pas impacter le build des autres projets, un profile JIRA peut être mis en place :

<profile>
      <id>JIRA</id>

      <repositories>
        <repository>
          <id>atlassian-public</id>
          <url>https://maven.atlassian.com/repository/public</url>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
            <checksumPolicy>warn</checksumPolicy>
          </snapshots>
          <releases>
            <enabled>true</enabled>
            <checksumPolicy>warn</checksumPolicy>
          </releases>
        </repository>
       <repository>
         <id>atlassian-plugin-sdk</id>
          <url>file://${env.ATLAS_HOME}/repository</url>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
          <releases>
            <enabled>true</enabled>
            <checksumPolicy>warn</checksumPolicy>
          </releases>
        </repository>
      </repositories>

      <pluginRepositories>
        <pluginRepository>
          <id>atlassian-public</id>
          <url>https://maven.atlassian.com/repository/public</url>
          <releases>
            <enabled>true</enabled>
            <checksumPolicy>warn</checksumPolicy>
          </releases>
          <snapshots>
            <updatePolicy>never</updatePolicy>
            <checksumPolicy>warn</checksumPolicy>
          </snapshots>
        </pluginRepository>
        <pluginRepository>
          <id>atlassian-plugin-sdk</id>
          <url>file://${env.ATLAS_HOME}/repository</url>
          <releases>
            <enabled>true</enabled>
            <checksumPolicy>warn</checksumPolicy>
          </releases>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
      <properties>
        <downloadSources>true</downloadSources>
        <downloadJavadocs>true</downloadJavadocs>
      </properties>
   </profile>

La commande complète pour utiliser la nouvelle configuration maven est la suivante :

atlas-debug -gs ~/.m2/settings.xml -P JIRA

Problèmes avec les repository maven d’entreprise (Nexus, Artifactory)

Dans les entreprises, il est courant d’avoir un repository maven permettant entre autre de :

  • Controler les dépendances utilisées par les applications
  • Conserver les dépendances dans le temps pour pouvoir reconstruire l’application
  • Publier les composants propres à l’entreprise
  • Accéder aux ressources d’internet par les proxy de l’entreprise

On a besoin d’ajouter les repositories Atlassian dans le repository d’entreprise pour permettre à tous les développeurs d’avoir la même configuration et au Jenkins de fonctionner.

En fonction de la configuration du repository d’entreprise, on peut avoir l’erreur suivante sur la dépendance

<groupId>asm</groupId>
<artifactId>asm-all</artifactId>
<version>3.0</version>

Le log de l’erreur :

Failed to transfer file: http://dsi.eutelsat.fr/artifactory/repo
/asm/asm-all/20070324/asm-all-20070324.pom'. Return code is: 409 , Re
asonPhrase:Conflict. -> [Help 1]

Le problème vient du fait que le composant a pour version 20070324 dans le nom du fichier et non 3.0 . Il faut dans ce cas désactiver les vérifications sur les dépendances dans le repository d’entreprise. Pour Artifactory c’est l’option Suppress POM Consistency Check

Suppression de la vérification de la dépendance dans artifactory

Voici les explications d’Artifactory sur le sujet.

Fast dev pour déployer rapidement son add-on

JIRA utilise avec beaucoup de succès la technologie OSGI pour installer à chaud (sans redémarrer le serveur) et isoler les plugins (classpath différent). Cette technologie offre aussi au développeur la possibilité de compiler et de redéployer le plugin sans redémarrer le serveur JIRA. Le temps de redéploiement sera alors entièrement dépendant de la taille du plugin.

Fastdev est activée quand on lance la commande atlas-debug.

Celui-ci permet de :

  • Vérifier automatiquement si du code a été changé
  • Recompiler et déployer automatiquement
  • Lancer des tests fonctionnels

Cette fonctionnalité est présente dans une barre en bas du navigateur qu’il faut déplier :

Ou trouver atlassian jira fastdev

Les fonctionnalités fastdev :

Les fonctionnalité jira fastdev

Problème rencontré :

J’ai eu le problème suivant sur le rechargement à chaud du plugin avec la version 5.0.13 de AMPS :

[INFO] Scanning for projects...
[WARNING] The POM for org.apache.maven.plugins:maven-compiler-plugin:jar:5.0.13 is missing, no dependency information available
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.759 s
[INFO] Finished at: 2015-06-05T16:47:07+01:00
[INFO] Final Memory: 13M/226M
[INFO] ------------------------------------------------------------------------
[ERROR] Plugin org.apache.maven.plugins:maven-compiler-plugin:5.0.13 or one of its dependencies could not be resolved: Failure to find org.apache.maven.plugins:maven-compiler-plugin:jar:5.0.13 in https://maven.atlassian.com/repository/public was cached in the local repository, resolution will not be reattempted until the update interval of atlassian-public has elapsed or updates are forced -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginResolutionException

Ma question et mon contournement sont disponibles ici

Tester le plugin

Les tests unitaires avec des mocks autour de l’API Jira sont relativement facile à mettre en place. Le code du plugin s’interface avec les APIs en utilisant la classe ComponentAccessor qui fournit le service. Mocker ComponentAccessor permettra d’intercepter les APIs que l’on veut simuler dans nos tests.

Les tests d’IHM “à la selenium” sont possible pour les plus courageux.

Les tests sur les interfaces métiers sont comme souvent les plus maintenables et pertinents surtout sur un plugin JIRA où l’objectif est de faire le moins de code possible en utilisant les APIs au maximum. Tester en simulant les APIs sera long, l’environnement de tests JIRA nous offre la possibilité de créer des tests métiers pertinents. Comment faire ?

Créer des composants

Les services métiers du plugin doivent être déclarés dans un composant JIRA qui permettra de séparer le modèle de la vue (pattern MVC).

Dans le atlassian-plugin.xml, il faut ajouter le composant comme ceci :

<component key="AppsonicAgeAnalysisComponentComponent"
               name="Appsonic aged balance component" i18n-name-key="appsonic-age-analysis-component.name"
               class="com.appsonic.jira.ageAnalysis.component.AgeAnalysisComponentImpl" public="true">
        <interface>com.appsonic.jira.ageAnalysis.component.AgeAnalysisComponent</interface>
    </component>

les tests ressemblent alors à cela :

@RunWith(AtlassianPluginsTestRunner.class)
public class AgeAnalysisComponentWithFilterWiredTest {
private final AgeAnalysisComponent ageAnalysisComponent;

    public AgeAnalysisComponentWithFilterWiredTest(AgeAnalysisComponent ageAnalysisComponent) {
        this.ageAnalysisComponent = ageAnalysisComponent;
    }


    @Test
       public void testGetWorkflowByIssue() throws SearchException {

        Assert.assertFalse(ageAnalysisComponent.getWorkflowByIssue(null).isEmpty());
    }    

}

L’annotation @RunWith(AtlassianPluginsTestRunner.class) indique qu’on utilisera les tests fonctionnels dans un serveur JIRA. On peut exécuter le tutorial officiel pour se lancer.

Injecter des données dans son test

Si on souhaite tester sur des projets JIRA contenant des demandes avec un workflow spécifique, par exemple, on peut injecter des données avant de lancer les tests de la façon suivante :

  1. Lancer JIRA atlas-run
  2. Créer un jeu de données (projets, workflow, demandes etc)
  3. Arrêter JIRA proprement (ctrl d)
  4. Exécuter la commande atlas-create-home-zip
  5. Copier le zip dans les ressources de tests
  6. Mettre à jour le pom.xml en indiquant où se trouvent les données à injecter
  7. Lancer la commande atlas-integration-test

Le pom.xml ressemble à cela :

<plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-jira-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <productDataPath>${basedir}/src/test/resources/generated-test-resources.zip</productDataPath>
                    <useFastdevCli>false</useFastdevCli>
                    <testGroups>
                        <testGroup>
                            <id>wired-integration</id>
                            <productIds>
                                <productId>jira</productId>
                            </productIds>
                            <includes>
                                <include>it/**/*WiredTest.java</include>
                            </includes>
                        </testGroup>
                        <testGroup>
                            <id>traditional-integration</id>
                            <productIds>
                                <productId>jira</productId>
                            </productIds>
                            <includes>
                                <include>it/**/*TrdTest.java</include>
                            </includes>
                        </testGroup>
                    </testGroups>
                </configuration>
            </plugin>

Pour exécuter des insertions en base, le mieux est de créer un projet sur chaque scenario. De cette façon les tests se lanceront à la suite, sans avoir à purger les données insérées (procédure de rollback)

Modifier et exécuter un test métier

Fastdev permet de voir le résultat des tests métiers et de les exécuter unitairement directement dans JIRA :

Open test fastdev

Voici la liste des tests avec une interface plutôt convainquante :

Check fastdev test result

Débuguer son plugin JIRA et ses tests

La commande atlas-debug ajoute automatiquement les paramètres permettant de prendre le contrôle de la JVM à distance.

Très pratique pour débuguer son code et les tests métiers dans un environnement JIRA avec son IDE préféré.

Ici la configuration avec intellij :

Debug JIRA avec Intellij

Les sources JIRA

L’API JIRA est documentée à l’aide d’une javadoc.

Avoir le code source JIRA sera très vite indispensable après le “hello word” pour comprendre les APIs JIRA.

Jira n’est pas open source, les sources sont téléchargeables sur le compte “licence Atlassian” disponible uniquement pour les développeurs qui ont acheté la version de JIRA correspondante (pas disponible sur l’abonnement cloud de JIRA).

Impossible d’avoir le code source sans licence, même avec un petit mail sympa, Atlassian ne veut pas.

Pour que l’IDE java du développeur puisse voir les sources sans avoir à les attacher à la main, il faut remplir le repository maven local ou le repository d’entreprise en utilisant le script fournit dans le zip des sources.

Et on a encore du travail car il va falloir :

  • Télécharger et installer manuellement 7 dépendances maven qui ne peuvent pas être téléchargé automatiquement pour des problèmes de licences (lire et respecter la documentation sur le sujet)
  • Compiler les sources en exécutant le script fournit par Atlassian (build.sh)
  • etc …

Le script va compiler tous les projets JIRA et associer les sources dans le repository maven local.

Il n’y aura plus qu’a faire “download source” dans l’IDE pour avoir les sources associées aux jars jira et recommencer l’opération à chaque nouvelle version de JIRA.

Problèmes rencontrés :

  • JIRA aime avoir sa propre configuration et repository Maven, le script build.sh n’échappe pas à cela, il faut éditer le build.sh pour utiliser le settings.xml et le repository local.
  • Dans la documentation, les scripts présents pour installer les composants en local ne fonctionnent pas “out of the box” car certaines librairies ont changé de nom/type (la documentation JIRA met souvent longtemps à se mettre à jour)
  • Le commande “mvn install:install-file” ne va pas forcément installer les librairies dans le bon repository local (HOME/.m2/repository). Le mieux est encore de le préciser via la commande :
mvn install:install-file -DgroupId=javax.activation -DartifactId=activation -Dversion=1.0.2 -Dpackaging=jar -Dfile=activation-1.0.2.jar -Dmaven.repo.local=SOURCE_JIRA/atlassian-jira-6.4-m14-source/localrepo
  • JIRA ne donne pas le code source de toutes les versions mineures, il faudra alors prendre les sources les plus proches et changer la version en utilisant le plugin maven set version

Comme on peut le constater, cette opération est longue et fastidieuse, je n’inclus plus les sources dans mon projet JIRA depuis le dernier debugueur intellij qui est capable de rendre lisible le bytecode java (fabuleux outil au passage, bravo JetBrains). En revanche, j’ai toujours un projet jira sous le coude pour m’inspirer du code.

Conclusion

Comme vous avez pu le lire, écrire un add-on Jira n’est pas aussi simple qu’on pourrait le penser. N’hésitez pas à laisser un commentaire sur ce billet si vous avez besoin d’aide ou me contacter.

Et vous pouvez aussi tester mon plugin JIRA “balance agée” en mettant des étoiles sur le store, ça me fera plaisir :)

Antoine



comments powered by Disqus