Pages

Thursday, August 28, 2008

Java, Moteur de template : Freemarker

Voici un moteur de template efficace : Freemarker

Le concept et de concevoir un template (un modèle contenant des "variables") :
inputTemplate.ftl :
<html>
<head>
<title>les Contacts</title>
</head>
<body>
<div>
</div>
Les contacts de ${carnet.proprietaire.prenom} ${carnet.proprietaire.nom}
<div>
<table>
<tr>
<td>nom</td>
<td>prenom</td>
</tr>
<#list carnet.lesContacts as unContact>
<tr>
<td>${unContact.nom}</td>
<td>${unContact.prenom}</td>
</tr>
</#list>
</table>
</div>
</body>
</html>
Construire une structure objet contenant les valeurs des variables à remplacer dans le template :
  Map root = new HashMap();

/* structure agenda */
Map agenda = new HashMap();
root.put("carnet", agenda);

/* structure proprietaire (dans agenda) */
Map proprietaire = new HashMap();
agenda.put("proprietaire", proprietaire);

/* remplissage de la structure proprietaire */
proprietaire.put("nom", "Dupont");
proprietaire.put("prenom", "Jean");

/* structure contacts (dans agenda */
List lesContacts = new ArrayList();
agenda.put("lesContacts", lesContacts);

/* remplissage des contacts */
Map unContact = new HashMap();
lesContacts.add(unContact);
unContact.put("nom", "Bond");
unContact.put("prenom", "James");

unContact = new HashMap();
lesContacts.add(unContact);
unContact.put("nom", "Woman");
unContact.put("prenom", "Cat");
Fusionner le template et la structure objet dans un fichier de destination :
   InputStreamReader fileTemplate = new InputStreamReader(ClassLoader
.getSystemResourceAsStream("inputTemplate.ftl"));
Configuration cfg = new Configuration();
cfg.setObjectWrapper(new DefaultObjectWrapper());
Template temp = new Template("inputTemplate.ftl", fileTemplate, cfg);

Writer out = new OutputStreamWriter(System.out);
temp.process(root, out);
out.flush();
out.close();
C'est évidemment un exemple assez simple mais les possibilités offertes par Freemarker sont très larges.

Pour plus d'informations : http://freemarker.sourceforge.net/

Wednesday, August 27, 2008

Java: Scanner un répertoire

Dans la cadre du développement de plugin maven 2, récupérer un ensemble de fichier selon certains critères peut être intéressant :
- récupérer un sous ensemble de fichiers source java.
- récupérer un ensemble de properties.
- ...

Plexus est là! Cette librairie est déjà largement utilisée par les plugins maven 2.

La classe DirectoryScanner vous permettra de faire ça.
Il faut lui spécifier :
- un répertoire de base.
- des patterns d'inclusions et d'exclusions (en options) du style "**/**.properties".
puis appeler la méthode scan() et récupérer ensuite un tableau avec l'ensemble des chemins des fichiers correspondant à la recherche.

Pour plus d'informations, tout est dans la javadoc de la classe.

wonderfull!

Thursday, August 21, 2008

requête XPath en java

Une solution simple (à mon goût) existe pour récupérer une valeur ou un attribut dans un fichier Xml en java sans avoir à le parcourir complètement avec Sax ou avec Dom.

Voici un petit exemple de mise en œuvre des APIS XPATH de java 1.5 s'appuyant sur Sax :

Voici le fichier XML source :
<?xml version="1.0" encoding="ISO-8859-1"?>
<level1>
<level2 level2-attribute1="valueOfLevel2-attribute1">
<level3 level3-attribute1="valueOfLevel3-attribute1">
<level41>valueOfLevel41</level41>
<level42>valueOfLevel42</level42>
</level3>
</level2>
</level1>
le code "technique" :

...
import java.io.InputStream;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.xml.sax.InputSource;

...

public static String executeXpath(InputStream sourceXml, String xPathQuery)
throws XPathExpressionException {

// creation de la source sax
InputSource source = new InputSource(sourceXml);

// creation de la requete XPTAH
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
XPathExpression xPathExpression = xpath.compile(xPathQuery);

/* execution de la requete xPath sur le fichier XML */
String resultat = (String) xPathExpression.evaluate(source,
XPathConstants.STRING);

return resultat;

}
...
}
La mise en œuvre :

public static void main(String[] args) {

try {
/* recuperation du flux xml */

InputStream xml = null;
String result = null;
String xPathQuery = null;

/* exemple 1 */
xml = Main.class.getResourceAsStream("/fichier.xml");
xPathQuery = "/level1/level2/@level2-attribute1";
result = XpathHelper.executeXpath(xml, xPathQuery);

System.out.println("xpathQuery : " + xPathQuery
+ "---> resultat : " + result);

/* exemple 2 */
xml = Main.class.getResourceAsStream("/fichier.xml");
xPathQuery = "/level1/level2/level3/level41";
result = XpathHelper.executeXpath(xml, xPathQuery);

System.out.println("xpathQuery : " + xPathQuery
+ "---> resultat : " + result);
} catch (XPathExpressionException e) {
e.printStackTrace();
}

}


Le résultat en console :
xpathQuery : /level1/level2/@level2-attribute1---> resultat : valueOfLevel2-attribute1
xpathQuery : /level1/level2/level3/level41---> resultat : valueOfLevel41


Le tout a le mérite d'être assez simple, élégant et performant.

Wednesday, July 23, 2008

Migration Maven 2 : 2ème épisode : la procédure

Suite de la migration... Comme je le précisais dans le première épisode dans ce post. J'ai procédé à la migration de gros applicatifs J2EE de maven 1 vers maven 2.

Une fois devant l'écran, un peu comme un écrivain, c'est un peu la page blanche : Par où commencer???

Je vais m'efforcer de vous décrire la procédure que j'ai appliquée:
  • Maitriser et comprendre l'applicatif et le processus de build maven 1 existant :
Avant tout, je pense qu'il faut bien connaître l'applicatif ainsi que le processus de build à migrer, en tous cas d'un point de vue technique. Cette connaissance va permettre de déceler les points sur lesquels il va falloir mettre l'accent plus particulièrement (où ça va coincer :-))
Dans mon cas, il se trouve que quasiment chaque projet java (l'applicatif est composé d'un ensemble de projets java) est muni de son project.xml mais surtout est quasiment toujours accompagné d'un fichier maven.xml. Pas de chance, il y a pas mal de spécificités de build qui ont été mises en œuvre : génération de code source en tout genre, concaténation de fichiers, exécution de tâches ant diverses et variées,...

  • Posséder une plateforme "témoin" :
Un des principales objectifs est de réussir à fournir, avec un processus de build différent, les mêmes livrables j2EE (un ear et plusieurs wars dans mon cas). Je me suis installé l'ensemble de la plateforme de développement et effectué des builds avec le processus de build maven 1 existant. Ce système permet d'avoir un environnement "témoin" permettant d'effectuer des comparaisons entre l'ancien build et le nouveau build. Concrètement, ça revient à récupérer 2 fois les projets de sources sur le poste:
- les projets de sources maven 1 "témoin".
- les projets de sources sur lesquels travailler pour effectuer la migration maven 2.

  • Migrer les dépendances
Dans un premier temps, il faut analyser et lister les dépendances dont le projet a besoin. Un bon vieux tableur facilite le travail. Pour chacune de ces dépendances, il faut vérifier si elle est disponible dans un repository maven 2 sur la toile grâce à ce site par exemple. Pour celles qui n'existent pas, il faut les installer dans le repository maven 2 de l'entreprise (s'il n'y en a pas, c'est un travail à faire... et pourquoi pas aussi mettre en œuvre Archiva , oulaaa, je m'emballe!) :
mvn deploy:deploy-file -Durl=file://C:\m2-repo \
-DrepositoryId=some.id \
-Dfile=your-artifact-1.0.jar \
[-DpomFile=your-pom.xml] \
[-DgroupId=org.some.group] \
[-DartifactId=your-artifact] \
[-Dversion=1.0] \
[-Dpackaging=jar] \
[-Dclassifier=test] \
[-DgeneratePom=true] \
[-DgeneratePom.description="My Project Description"] \
[-DrepositoryLayout=legacy] \
[-DuniqueVersion=false]

  • Mettre en place un premier pom.xml
Dans le cadre d'applicatif j2EE découpé en un ensemble de projets Java/Maven, il est difficile de migrer l'ensemble des POM en une seule fois. Il est important de procéder de façon itérative et de se concentrer sur la migration d'un des projets (simple en terme de build si possible) pour commencer.

  • Capitaliser et centraliser le build
Les possiblités en terme d'héritage et de gestion "multi module" de Maven permettent :
- de réutiliser comportement de Pom en définissant des Pom parent, grand-parent, ...
- de piloter l'ensemble des projets en définissant un Pom multimodule.

En conservant à l'esprit ce type de pratique, j'ai progressivement procédé à des refactorings de Pom au fur et à mesure de la mise en place des Pom des projets. Voici une version simplifiée de mon arborescence de Pom :
pom "base"
pom projet utilitaire
...
pom "webapp"
pom webapp1
pom webapp2
...
pom "ejb"
pom ejb1
pom ejb2
...
  • Migrer les spécificités maven 1 : "exit maven.xml"
Une fois le comportement global du build définit, les spécificités de build maven 1 viennent vite bloquer le processus de migration, notamment en ce qui concerne la génération de code qui a un impact sur la compilation (ça compile si le code a été généré sinon non...).
Voici quelques exemples de spécificités :
- génération des classes techniques ejb avec ejbDoclet de XDoclet : cette génération était pilotée en Maven 1 par le lancement de tâche Ant. Le plugin maven2 antrun a permis de faire la migration.
- génération d'un fichier par concaténation d'un ensemble de fichiers : La concaténation était effectuée en jelly... La seule solution était de mettre en place un plugin maven 2 qui effectue le travail.

  • Rester zen...
Une fois passé le cap du "BUILD SUCCEFULL" (qui , d'ailleurs, fait bien plaisir), le gros du travail est effectué. Le reste correspond a des ajustements mais des ajustements qui font d'ailleurs perdre pas mal de temps. Dans mon cas , le build du projet dans son ensemble prend dans les 10 minutes. Pour tester une modification, une seule solution : reBUILDER! Il faut savoir être patient et se dire que si on devait le faire soi-même on mettrait 3h pour faire un seul build...

  • Comparer les livrables j2EE entre le build maven 1 témoin et le nouveau build maven2
Les principaux ajustements à effectuer pour obtenir des contenus de livrables (ear et wars) identiques concernent les dépendances dans les archives war et ear. Avec maven 2 et la transitivité des dépendances, j'ai dû ajuster les choses en terme de versions et de porter de jars pour finalement obtenir des dossiers de "lib" identiques.

  • Tester les nouveaux livrables
Il faut ensuite déployer les nouveaux livrables pour valider que l'application fonctionne toujours... et procéder à des ajustements en terme de fichiers de conf J2EE notamment ceux propres au serveurs d'applications (ex : weblogic.xml, jboss-web.xml, ...).

Une fois ce cap franchi, on pense avoir quasiment terminé... mais il reste des choses à faire...
Ha, mon build vient de se terminer, la suite dans le prochain épisode...

Tuesday, July 15, 2008

Migration Maven 2 : 1ère épisode : Le contexte

Je me suis attelé ses derniers jours (voir mois) chez mon client à entreprendre la migration de gros applicatifs maven 1 vers maven 2. Je vais essayer de vous faire profiter d'un retour d'expérience sur le sujet car je n'ai pas trouvé de méthodes miracles de migration.

Je vais y consacrer un certains nombres de billets. Le dossier est assez touffu...

Pour commencer, voici le contexte des applicatifs qui doivent être migrer :
  • applicatifs j2EE multi module (ear contenant un ensemble d'ejb sessions + mutli war).
  • héritage de projet maven.
  • utilisation du multi-projet.
  • changement du type de packaging (simple jar, ejb-jar ou ejb-jar-client) en fonction de propriétés au moment du build.
  • génération des classes techniques des ejbs avec xdoclet (ejbdoclet).
  • génération de classes techniques type "delegate" pour simplifier l'appel aux ejbs avec xdoclet (template doclet).
  • génération des sites projets.
  • utilisation de l'intégration continue avec continuum.
  • dans les 8000 fichiers au contrôle de source (sources + ressources).
  • une dizaine de minutes à attendre pour faire un build complet.
  • entre 5 et 20 développeurs par applicatifs.
  • déjà en production pour certains applicatifs.
  • application de type stratégiques.

Avant tout on peut se demander pourquoi? Voici les arguments que j'ai pu trouver à part de dire qu'il faut le faire :-) :
  • Pour permettre d’utiliser les nouvelles fonctionnalités de Maven2 (transitivité des dépendances, profile, scope, …).
  • Pour profiter des modifications effectuées sur les fonctionnalités existantes (gestion multiprojet, héritage).
  • Pour profiter des améliorations de performances que propose maven 2 (consommation mémoire pendant le build, temps de build …).
  • De simplifier les scripts de build.

Je détaillerai par la suite la démarche de migration qui a été mise en œuvre dès que j'ai un peu de temps entre 2 builds :-).

La suite dans le prochaine épisode...