Article publié par Benoit Moussaud le 16 décembre 2008.
Catégorie(s) : Java / JEE
Je vais vous révéler un secret à propos de notre célèbre Revue de Presse ! Elle est élaborée collectivement par l’ensemble des consultants de Xebia sous Confluence avant d’être publiée sur notre blog. L’utilisation d’un wiki permet de suivre facilement les différentes modifications (contribution, corrections, commentaires). Une fois celle-ci terminée, la page est archivée et une autre est créée, vide, afin de recevoir la prochaine version. La fin de l’année arrive, c’est l’heure de faire les statistiques 2008. L’une des questions que je me pose est « Quelle est la proportion des entrées de la revue de presse entre les différentes catégories (Actualité éditeurs / SSII, Agilité, RIA, SOA, …) ? »
Solution N°1 : Ouvrir dans le navigateur toutes les pages ‘Revue de Presse’ une par une et compter les entrées. Une revue de presses par semaine, 52 semaines … non
Solution N°2 : Utiliser une API. En effet Confluence expose ses principales fonctions par web services. Voici le wsdl. Plus de 7300 lignes, 238 opérations, 50 types. À ce moment-là, j’étais presque prêt à repasser à la solution N°1 quand je suis tombé au détour d’une recherche Google (Google est notre ami à tous !), sur le projet Swizzle…
Swizzle est un wrapper Java autour de l’api XML-RPC proposée par Confluence.
Installation
Rapide ! Swizzle est disponible dans le repository Maven
<dependency>
<groupId>org.codehaus.swizzle</groupId>
<artifactId>swizzle-confluence</artifactId>
<version>1.1</version>
</dependency>
Principe
- Une seule classe,
org.codehaus.swizzle.confluence.Confluence, permet d’accéder aux différents services. - Un ensemble de POJO qui servent de paramètres d’entrée et de retour. Exemple :
org.codehaus.swizzle.confluence.PageSummary.
Se connecter
String endpoint = "https://the.confluence.of.xebia.com/confluence/rpc/xmlrpc";
confluence = new Confluence(endpoint);
System.out.println("Connected to " + endpoint);
String username = "bmoussaud";
String password = "tiger";
confluence.login(username, password);
Si vous êtes dérrière un proxy, initialiser les variables d’environnement suivantes:
System.getProperties().put("proxySet", "true");
System.getProperties().put("proxyHost", "17.240.34.194");
System.getProperties().put("proxyPort", "3128");
Retrouver une page
Les Revues de Presse sont archivées, comme l’ensemble du blog, par trimestre. Il faut donc :
- Retrouver la page correspondant à ce trimestre.
- Récupérer les pages filles.
- Filtrer pour ne garder que les articles « Revue de Presse ».
public List<PageSummary> getRevuesDePresse(String quarter) throws Exception {
List<PageSummary> results = new ArrayList<PageSummary>();
Page page = confluence.getPage("Espace Xebia France", quarter); //#1
List<PageSummary> children = confluence.getChildren(page.getId()); //#2
for (PageSummary pageSummary : children) {
if (pageSummary.getTitle().contains(REVUE_DE_PRESSE)) { //#3
results.add(pageSummary);
}
}
return results;
}
- Une page est identifiée par son Id représenté par un type
java.lang.String. - La classe
PageSummarycontient les informations minimales d’une page :Id,ParentId,Title,URL. - La classe
Pagecontient toutes les informations de la page, y compris son contenu.
Analyser le contenu d’une page
La Revue de Presse contient 6 catégories principales (Actualité éditeurs / SSII, Agilité, RIA, SOA, …). Elles sont hiérarchisées en niveau 3 (syntaxe wiki h3.). Les différentes entrées de la revue de presse sont de niveau 4 (syntaxe wiki h4.). Il faut donc :
- Récupérer le contenu de la page.
- Filtrer les titres de niveau 3 et 4.
- Effectuer le comptage.
public Map<String, Integer> countArticleByCategory(String pageId)
throws Exception {
String content = confluence.getPage(pageId).getContent(); // #1
String currentCategory = null;
String currentEntry = null;
Pattern h3h4 = Pattern.compile("(^h3|^h4)
.(.*)",
Pattern.MULTILINE); // #2
Matcher matcher = h3h4.matcher(content);
Map<String, Integer> categories = newMap();
while (matcher.find()) {
//entrées vides
if (matcher.group().contains("...")
|| matcher.group().contains(".."))
continue;
// niveau 3
if ("h3".equals(matcher.group(1))) { // #2
currentCategory = matcher.group(2).trim();
categories.put(currentCategory, 0);
continue;
}
// niveau 4
if ("h4".equals(matcher.group(1))) { // #2
currentEntry = matcher.group(2);
if (currentCategory != null
&& categories.containsKey(currentCategory)) {
int c = categories.get(currentCategory);
categories.put(currentCategory, ++c); // #3
}
}
}
return categories;
}
Conclusion
Il suffit maintenant :
- D’assembler les deux méthodes.
- De dumper les informations au format CSV.
- D’ouvrir Excel.
- De construire un tableau croisé dynamique.
et vous n’aurez pas les résultats !!
Pour en revenir à Swizzle / Confluence : l’encapsulation proposée est correcte et performante. Le seul point négatif : toutes les méthodes de la classe Confluence lancent des java.lang.Exceptions, non typées. Elles sont dans l’ensemble le résultat d’un comportement de type Runtime. J’aurais donc préféré une signature finissant par throws RuntimeException.
L’api permet également :
- D’obtenir des informations sur l’historique des pages et des contributeurs, les commentaires, l’état de la page (nombre de verrous, supprimées, …).
- De créer, déplacer et détruire des pages et des espaces.
Exemple : archivage des revues de presse :
void run(String newParent) throws Exception {
System.out.println("Archivage des Revues de Presse dans "+newParent);
myConfluence = new MyConfluence();
List<PageSummary> draftedRevuesDePresse = myConfluence.getDraftedRevuesDePresse();
int count = 0;
for (PageSummary pageSummary : draftedRevuesDePresse) {
if (pageSummary.getTitle().startsWith("DRAFT"))
continue;
myConfluence.moveTo(pageSummary,newParent);
}
System.out.println("Total "+count);
}
//....
public void moveTo(PageSummary pageSummary, String newParent)
throws Exception {
Page targetPage = confluence.getPage("XF", newParent);
Page page = confluence.getPage(pageSummary.getId());
System.out.println("Change Parent from '"
+ confluence.getPage(page.getParentId()).getTitle() + "' to '"
+ targetPage.getTitle() + "'");
page.setParentId(targetPage.getId()); //Changement de Parent
confluence.storePage(page); //Applique les modifications
}
Swizzle propose également un wrapper sur les API exposées par Jira.
Note: Swizzle ne fonctionne que si l’option « API à distance (XML-RPC & SOAP) » est activée.


Complet
Twitter







WSDL qui pousse à faire du JAX-RPC? Pouah!
A quand une interface JAX-RS?
Et le résultat des statistiques ?
A quand un bêtisier 2008 sur les perles de la programmation comme l’an dernier ?
Nicolas