Logging tomcat et hibernate

Introduction

Dans une application d'un client que je reprends pour de la maintenance, j'ai un problème. Cette application est faite pour Oracle et, sur ma machine de test, je n'ai aucune envie d'installer cette base de données plutôt gourmande en ressources.

Cette application utilise l'ORM hibernate pour abstraire tous les accès à la base de données. C'est donc, normalement, un jeu d'enfant d'utiliser un au SGBD. J'ai donc décidé d'utiliser le MySQL 5.1.36 qui est installé sur ma machine. Ensuite, j'ai modifié le fichier "persistence.xml" pour qu'hibernate se connecte à une nouvelle base de données sous MySQL.

Problème

Alors que tout devrait fonctionner sans problème, en lançant l'application, je n'ai aucune table qui est créée.

Dans le logging de cette application, j'obtiens l'erreur suivante :

ERROR org.hibernate.tool.hbm2ddl.SchemaUpdate - Unsuccessful: alter table nom_de_la_table add index FKCD10932052C35B33 (clé), add constraint FKCD10932052C35B33 foreign key (clé) references preview (id)
ERROR org.hibernate.tool.hbm2ddl.SchemaUpdate - Can't create table 'nom_de_la_table' (errno: 150)

Logging

Bon, le fait d'avoir un message d'erreur ne me pose pas plus de problèmes que ça. Par contre, ce qui m'embête c'est le fait que ce message est plutôt vague. Il me faut donc avoir un logging plus détaillé.

persistence.xml

Pour ce faire, j'ai activé les logging de requêtes dans persistence.xml

 

C'est un bon début, mais je ne vois toujours pas les requêtes de création de tables qui m'intéressent...

log4j.properties

La première chose à faire est d'ajouter un fichier "log4j.properties" au bon endroit dans ce projet qui utilise "Maven" : Voici le contenu du fichier. J'y suis allé franchement en mettant les niveaux de logging à "debug" de manière à récupérer un maximum d'informations.

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 
log4j.rootLogger=warn, stdout
log4j.logger.org.hibernate=debug, stdout
log4j.logger.org.hibernate.SQL=debug, stdout
log4j.logger.org.hibernate.type=debug, stdout
log4j.logger.org.hibernate.engine.QueryParameters=debug, stdout

Solution

En fouillant dans ce logging très détaillé, je trouve le problème suivant :

09:12:59,704 DEBUG SchemaExport:415 -
    create table NOM_DE_LA_TABLE (
        CLE integer not null,
        ...
        primary key (CLE)
    ) type=InnoDB
Cette requête n'est pas correcte. "type=InnoDB" ne fonctionne pas avec ma version de MySQL, il faudrait "Engine=InnoDB". Je dois donc, simplement, changer le dialecte SQL :
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>

Conclusion

En fait, j'avais créé moi-même le problème en utilisant le mauvais dialecte MySQL. Mais, sans un bon système de logging, je n'avais aucun moyen de m'en apercevoir. Je me dis que ça peut arriver à n'importe qui...

Essai d’utilisation de la bibliothèque « KineticJS » dans un projet GWT (Google Web Toolkit)

Introduction

Le but de cet article est de tester la bibliothèque "KineticJS". Celle-ci bénéficie d'une meilleure intégration à GWT que "D3.js" car un wrapper "KineticGWT" existe.

Du coup elle devrait la remplacer avantageusement dans un projet de ce type.

Projet de test

Pour générer le projet de test je vais utiliser un archetype Maven pour générer le squelette du projet.

mvn archetype:generate \
   -DarchetypeGroupId=org.codehaus.mojo \
   -DarchetypeArtifactId=gwt-maven-plugin \
   -DarchetypeVersion=2.5.0
  • groupId: conceptforge.kinetics
  • artifactId: TestKinetics
  • version: 1.0-SNAPSHOT
  • package: conceptforge.kinetics
  • module: TestKinetics

A ce stade, le projet est fonctionnel, mais c'est le projet par défaut généré par l'archetype Maven.

Après une bonne cure d'amaigrissement, le projet est devenu un magnifique "Hello World" à partir duquel je vais pouvoir commencer à intégrer "KineticJS".

Voici un aperçu des fichiers qui restent dans le projet :

Le code source complet de ce Hello World est disponible ici : TestKinetics1.tar.gz

Intégration de la librairie KineticsJS

Le portage de cette librairie sous GWT porte (naturellement) le nom de "KineticGWT". Le lien "github" met à disposition de la documentation pour l'intégration de cette librairie dans une application GWT/Maven. Ça tombe bien, c'est exactement le cas de ce programme de test.

Il faut donc :

  • Ajouter les nouvelles dépendances dans "pom.xml"
  • Ajouter le module dans "TestKinetics.gwt.xml"
  • Modifier l'implémentation de "onModuleLoad()"
package conceptforge.kinetics.client;
 
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.RootPanel;
import net.edzard.kinetic.*;
 
/**
 * Entry point classes define
 * <code>onModuleLoad()</code>.
 */
public class TestKinetics implements EntryPoint {
 
    /**
     * This is the entry point method.
     */
    public void onModuleLoad() {
        // Kinetic needs a special div in the DOM
        Element div = DOM.createDiv();
        RootPanel.getBodyElement().appendChild(div);
 
        // Setup stage
        Stage stage = Kinetic.createStage(div, 400, 400);
        Layer layer = Kinetic.createLayer();
        stage.add(layer);
 
        Rectangle c = Kinetic.createRectangle(new Box2d(10, 10, 200, 200));
        layer.add(c);
        stage.draw();
    }
}

Résultat

Site du zéro

Par chance le site du zéro propose un tutoriel sur KineticsJS.

Formes prédéfinies

Voici un exemple librement inspiré de la première partie du tutoriel (formes prédéfinies)

package conceptforge.kinetics.client;
 
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.RootPanel;
import net.edzard.kinetic.*;
 
/**
 * Entry point classes define
 * <code>onModuleLoad()</code>.
 */
public class TestKinetics implements EntryPoint {
 
    /**
     * This is the entry point method.
     */
    public void onModuleLoad() {
        Element div = DOM.createDiv();
        RootPanel.getBodyElement().appendChild(div);
 
        Stage scene = Kinetic.createStage(div, 400, 400);
        Layer calque = Kinetic.createLayer();
        scene.add(calque);
 
        Rectangle r = Kinetic.createRectangle(new Box2d(50, 75, 350, 300));
        r.setFill(Colour.blue);
        r.setStroke(Colour.blue);
        r.setStrokeWidth(0);
 
        Circle c = Kinetic.createCircle(new Vector2d(50, 50), 50);
        c.setFill(Colour.green);
        c.setStroke(Colour.darkgray);        
 
        RegularPolygon h = Kinetic.createRegularPolygon(new Vector2d(200,150), 50, 6);
        h.setRotation(30);
        h.setFill(Colour.red);
 
        calque.add(r);
        calque.add(c);
        calque.add(h);
 
        scene.draw();
    }
}

Résultat

Ombres

En m'inspirant du tutoriel "Opacité et ombre", j'ai ajouté une ombre aux formes de l'exemple précédent :

Pour arriver à ce résultat il suffit de créer l'instance suivante de "Shadow":

Shadow shadow = new Shadow(Colour.black, 12, new Vector2d(8, 8), 0.7);

Ensuite, il faut utiliser la méthode "setShadow" pour passer cette valeur à toutes les formes.

Gestion des événements

En m'inspirant du tutoriel "Interactions", j'ai écrit le code suivant pour observer le contenu de la variable "evt" passée en paramètre dans la gestion d'événements :

r.addEventListener(Event.Type.CLICK, new Node.EventListener() {
 
            public boolean handle(Event evt) {
                try {
                    Window.alert(evt.toString());
                } catch (Exception e) {
                    Window.alert(e.getMessage());
                }
                return true;
            }
        });

En testant, j'ai eu la désagréable surprise de recevoir le message suivant :

Grosse déception

Alors que tous mes tests jusqu'à maintenant me montraient une bonne librairie pour manipuler la balise Canvas et un bon wrapper pour GWT. Je viens de toucher aux limites de cette implémentation.
Lors de la gestion d'événements, la méthode "handle" d'un EventListener contient une variable evt mais celle-ci contient toujours "null".
Après une petite recherche dans le code source, j'ai trouvé (dans l'implémentation de la classe "Node") la méthode suivante qui explique mon problème :

	/**
	 * Add a multi-event listener to the node.
	 * @param eventTypes A number of events to listen to
	 * @param handler The handler.
	 */
	// Seems to be buggy in kineticjs. Always getting mousemove events
	// TODO Maik: It's not buggy, it just fails and then creates too many events due to the failure.
	//             the evt variable is directly from the browser, not from kineticjs, that's why there's
	//             a mismatch between event types
	//             Touch also needs to be handled differently if evt should be used.
	//       quick fix: ignore evt object
	public final native void addEventListener(List eventTypes, EventListener handler) /*-{
		this.on(@net.edzard.kinetic.Node::createEventTypeString(Ljava/util/List;)(eventTypes), function(evt) {
//			if (evt != null) {
//				console.log(evt.type);
//				var javaEvt = @net.edzard.kinetic.Event::new(Lnet/edzard/kinetic/Event$Type;Lnet/edzard/kinetic/Event$Button;II)(
//					@net.edzard.kinetic.Event.Type::valueOf(Ljava/lang/String;)(evt.type.toUpperCase()),
//					@net.edzard.kinetic.Event.Button::fromInteger(I)(evt.button),
//					evt.offsetX,
//					evt.offsetY
//				);
//				javaEvt.@net.edzard.kinetic.Event::setShape(Lnet/edzard/kinetic/Shape;)(evt.shape);
//				var bubble = handler.@net.edzard.kinetic.Node.EventListener::handle(Lnet/edzard/kinetic/Event;)(javaEvt);
//				evt.cancelBubble = !bubble;
//			} else {
				handler.@net.edzard.kinetic.Node.EventListener::handle(Lnet/edzard/kinetic/Event;)(null);
//			}
		});
	}-*/;

En attendant une correction de cette méthode, je propose une solution de contournement (pas très élégante, forcément) :

r.addEventListener(Event.Type.CLICK, new Node.EventListener() {
 
            public boolean handle(Event evt) {
                if (r.getFill().toString().equals(Colour.blue.toString())) {
                    r.setFill(Colour.white);
                } else {
                    r.setFill(Colour.blue);
                }
 
                scene.draw();
                return true;
            }
        });

Je fais un accès direct à l'objet. Ce n'est pas élégant et c'est beaucoup moins souple, mais, au moins, c'est fonctionnel.
En écrivant cette petite routine, j'ai eu une deuxième mauvaise surprise, mais moins grave que la précédente. La comparaison de la couleur ne fonctionne ni avec "==" ni avec "equals".

Conclusion

Tout s'est très bien passé jusqu'à ce que j'essaye de gérer les événements. Mon soupçon est que la librairie "KineticJS" est très fonctionnelle. Ce soupçon n'est pas fondé sur des arguments techniques, mais plutôt construit à partir des différents commentaires que j'ai pu lire à ce sujet sur le Web.

Par contre, le wrapper "KineticGWT" m'a permis de réaliser très facilement les exemples du tutoriel que j'ai utilisé tant que je ne faisais pas de gestion d'événements. Pour l'instant, il n'est qu'en version "0.9.1" c'est donc certainement un bug de jeunesse, mais à ce stade je ne sais pas si j'ose prendre le risque d'intégrer ce wrapper dans un projet réel et contourner tous les petits problèmes résiduels...

Représentation de données arborescentes à l’aide de D3.js

Introduction

Le but de cet article est de mettre en oeuvre la librairie D3.js pour représenter des données sous forme d’arborescence.

Données

Les données que je souhaite exploiter dans cet exemple sont stockées dans une base de données. La table "MenuItem" contient les éléments suivants :

Le champ "PARENT_ITEM" contient le "ID_MENU_ITEM" d'un élément de menu d'un ordre supérieur.

Cette simple table permet de stocker des menus sous forme arborescente.

Extraction

Etant donné que ces données sont déjà largement utilisées dans des projets Java, je vais écrire un petit programme de test en réutilisant au maximum les routines existantes. Mon but est d'obtenir un fichier JSON pour que D3.js puisse le représenter .

Maven

Dans les projets de production, Maven a été massivement utilisé pour lier les différents librairies et sous-projets. Je vais donc aussi utiliser Maven pour mon projet de test et ainsi importer facilement les librairies dont j'ai besoin et surtout l'accès aux données via hibernate qui va largement me simplifier la vie.

pom.xml

Ce fichier me permet de lier les librairies dont j'ai besoin :

  • JUnit : Pour les tests unitaires.
  • NavigationEntity : Librairie "maison" contenant les entités hibernate en rapport avec les données qui permettent à l'utilisateur de "naviguer" dans le programme.
  • Guice : Outil d'injection de dépendances.
  • Mysql-Connector : Pour accéder à mes données de test
  • XStream : Outils de sérialisation d'objets
  • Jettison : Librairie qui permet à XStream de sérialiser les données au format JSON
 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>ch.conceptforge.group</groupId>
    <artifactId>json_d3</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>json_d3</name>
    <url>http://maven.apache.org</url>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>NavigationEntity</artifactId>
            <version>2.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject.extensions</groupId>
            <artifactId>guice-persist</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.14</version>
        </dependency> 
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jettison</groupId>
            <artifactId>jettison</artifactId>
            <version>1.3.2</version>
        </dependency>        
    </dependencies>
</project>

Hibernate

Je ne vais pas entrer trop dans les détails, car c'est trop spécifique à la librairie maison "NavigationEntity". Celle-ci utilise Hibernate et Guice pour se connecter à la base de données et extraire les entités sous forme d'objets. Ceci nécessite de créer un fichier "persistence.xml" et de le configurer pour pouvoir utiliser la classe "MenuItem" qui encapsule les données présentées précédemment dans des objets.

Guice

De même que pour hibernate, cette partie est propre à la librairie maison "NavigationEntity", donc je ne vais pas entrer trop dans les détails. Je vais simplement préciser qu'il est nécessaire de construire une classe qui implémente l'interface "Module" de guice. Pour plus d'informations à ce propos, je vous invite à consulter les documents suivants :

Programme principal

package ch.conceptforge.paquet;
 
import ch.conceptforge.paquet.MenuItemTree;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
import java.io.Writer;
import javax.persistence.EntityManager;
 
public class App
{
 
    private static EntityManager createEntityManager() {
        Injector injector = Guice.createInjector(new GuiceModule());
        return injector.getInstance(EntityManager.class);
    }
 
    private static XStream createXStream() {
        XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
 
            @Override
            public HierarchicalStreamWriter createWriter(Writer writer) {
                return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
            }
        });
 
        return xstream;
    }
 
    public static void main( String[] args )
    {
        // Liaison de l'entityManager à la classe MenuItem
        MenuItemTree.setEm(createEntityManager());
 
        // Construction de l'arborescence
        MenuItemTree mit = new MenuItemTree();
 
        // Exportation de l'arbre en JSON sur la console
        System.out.println(createXStream().toXML(mit));
    }
}

La partie la plus importante de ce petit programme de test est la construction de l'instance de "MenuItemTree". Le code qui précède cette instanciation permet d’accéder aux entités hibernate. Le passage de l'entityManager par une méthode statique n'est pas élégant, mais, s'agissant d'un petit programme de test, je ne vais pas y remédier. Le code qui suit l'instanciation de "MenuItemTree" permet de sérialiser cet objet. Il suffira ensuite de mettre cette chaine de caractères dans un fichier texte et de la lier à D3.js.

MenuItemTree

Cette classe va s'occuper d'interroger la base de données et de construire l’arborescence d'objets. Dans un esprit de réutilisabilité, j'ai découpé cette classe en deux niveaux d'abstraction.

Tree

Cette classe contient la structure de base d'un arbre qui pourra être utilisé par D3.js

package ch.conceptforge.paquet.tree;
 
import java.util.ArrayList;
import java.util.List;
 
public class Tree {
 
    private String name;
    private List children = null;
 
    public Tree() {
 
    }        
 
    public Tree(String name) {
        this.name = name;
    }
 
    protected void addChild(Tree tree) {
        if (children == null) {
            children = new ArrayList();
        }
        children.add(tree);
    }
 
    public List getChildren() {
        return children;
    }
}

MenuItemTree

Cette classe contient l'implémentation concrète de la structure arborescente pour mes MenuItems.

package ch.ultrasoft.cortex.tree;
 
import com.ultrasoft.server.core.entity.navigation.MenuItem;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
 
/**
 *
 * @author silenus
 */
public class MenuItemTree extends Tree {
 
    private static EntityManager em;        
 
    public static EntityManager getEm() {
        return em;
    }
 
    public static void setEm(EntityManager em) {
        MenuItemTree.em = em;
    }           
 
    public MenuItemTree() {
        super();
        parseItems(em.createQuery("from MenuItem where parentItem = null"));
    }
 
    public MenuItemTree(int id, String name) {
        super(name);
        parseItems(em.createQuery("from MenuItem where parentItem = " + id));
    }
 
    private void parseItems(Query q) {
        for (MenuItem mi : (List) q.getResultList()) {
            addChild(new MenuItemTree(mi.getId(),mi.getText()));
        }
    }
}

JSON

Comme expliqué précédemment, ce petit programme affiche sur la console les données au format json. Voici un extrait :

{
  "children": [
    {
      "name": "Stammdaten",
      "children": [
        {
          "name": "Adressetyp"
        },
        {
          "name": "Bauart"
        },
        {
          "name": "Betriebstyp"
...
        }
      ]
    }
  ]
}

La totalité des données est stockée dans le fichier test2.json

D3.js

A partir des nombreux exemples d'utilisation de cette librairie, voici quelques propositions d'affichage pour mon arborescence.

Le code html n'est pas particulièrement soigné, je souhaitais dans cet article me concentrer sur le rendu visuel des différentes variantes. Au final, une seule sera choisie pour être implémentée dans une application. C'est à ce moment-là que le code sera repris en profondeur.

Maven/Netbeans : Tutoriel simple

Introduction

Prochainement, j'ai envie de présenter quelques tutoriels Java. Un prérequis à ces futurs tutoriels sera l'utilisation de Maven, c'est pourquoi je commence par présenter ce formidable projet. Même si Maven est un sujet maintes fois abordé, je vais en présenter ma propre variante. Plus d'un point de vue pragmatique que théorique.

Documentation

Quitte à passer pour un dinosaure, quand je m'intéresse à un nouveau jouet technologique, j'aime bien lire le mode d'emploi. Dans le cas de Maven, le mode d'emploi que j'ai utilisé est le livre suivant :

Titre : Apache Maven

Auteurs : Nicolas De loof et Arnaud Héritier

Amazon : Apache Maven

Ce livre est très agréable à lire. Il est rédigé dans un style "détendu" tout en fournissant les informations nécessaires à la compréhension et à l'utilisation de Maven.

Il se trouve que les différents sujets et technologies abordés à fil des pages et très proche de plusieurs projets sur lesquels j'ai travaillé ces dernières années:

  • Systèmes de gestion des versions (notamment SubVersion et Git)
  • Testing (JUnit, TestNG et Selenium)
  • Google Web Toolkit (GWT)
  • Serveurs d'application Web Java (Tomcat, Jetty)

Mise en oeuvre de Maven

L'EDI que j'utilise le plus souvent pour du développement Java est Netbeans. Cet environnement de développement peut ouvrir un projet ayant la structure de fichiers Maven et intègre tous les outils nécessaires aux tâches courantes d'un programmeur.

Netbeans 7.1

Création, sous Netbeans, d'un nouveau projet Java en utilisant Maven. Ayant pour habitude de faire des "Hello World" à tout vent, en voici un de plus, le 5ème ces derniers temps ;-)

Création du projet helloworld5


Sorties à la création du projet

cd /Users/silenus/NetBeansProjects; JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home "/Applications/NetBeans/NetBeans 7.1.2.app/Contents/Resources/NetBeans/java/maven/bin/mvn" -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.1 -DarchetypeRepository=http://repo.maven.apache.org/maven2 -DgroupId=ch.conceptforge -DartifactId=helloworld5 -Dversion=1.0-SNAPSHOT -Dpackage=ch.conceptforge.helloworld5 -Dbasedir=/Users/silenus/NetBeansProjects -Darchetype.interactive=false --batch-mode archetype:generate
Scanning for projects...
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-deploy-plugin/2.7/maven-deploy-plugin-2.7.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-deploy-plugin/2.7/maven-deploy-plugin-2.7.pom (6 KB at 12.5 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at 108.8 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom (26 KB at 150.5 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom (15 KB at 112.9 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-deploy-plugin/2.7/maven-deploy-plugin-2.7.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-deploy-plugin/2.7/maven-deploy-plugin-2.7.jar (27 KB at 149.7 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-site-plugin/3.0/maven-site-plugin-3.0.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-site-plugin/3.0/maven-site-plugin-3.0.pom (20 KB at 165.2 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-site-plugin/3.0/maven-site-plugin-3.0.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-site-plugin/3.0/maven-site-plugin-3.0.jar (112 KB at 460.4 KB/sec)
 
------------------------------------------------------------------------
Building Maven Stub Project (No POM) 1
------------------------------------------------------------------------
 
&gt;&gt;&gt; maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom &gt;&gt;&gt;
 
&lt;&lt;&lt; maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom &lt;&lt;&lt;
 
[archetype:generate]
Generating project in Batch mode
Archetype defined by properties
----------------------------------------------------------------------------
Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
----------------------------------------------------------------------------
Parameter: groupId, Value: ch.conceptforge
Parameter: packageName, Value: ch.conceptforge.helloworld5
Parameter: package, Value: ch.conceptforge.helloworld5
Parameter: artifactId, Value: helloworld5
Parameter: basedir, Value: /Users/silenus/NetBeansProjects
Parameter: version, Value: 1.0-SNAPSHOT
********************* End of debug info from resources from generated POM ***********************
project created from Old (1.x) Archetype in dir: /Users/silenus/NetBeansProjects/helloworld5
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 3.814s
Finished at: Sun Aug 26 20:41:03 CEST 2012
Final Memory: 9M/81M
------------------------------------------------------------------------


Cool, ils ont pensé à moi chez Sun Oracle. Mon "Hello World" est déjà écrit.

Un petit run

Le "run project (F6)" lance le projet comme s'il s'agissait d'un projet Java standard :

cd /Users/silenus/NetBeansProjects/helloworld5; JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home "/Applications/NetBeans/NetBeans 7.1.2.app/Contents/Resources/NetBeans/java/maven/bin/mvn" "-Dexec.args=-classpath %classpath ch.conceptforge.helloworld5.App" -Dexec.executable=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java -Dexec.classpathScope=runtime process-classes org.codehaus.mojo:exec-maven-plugin:1.2:exec
Scanning for projects...
 
------------------------------------------------------------------------
Building helloworld5 1.0-SNAPSHOT
------------------------------------------------------------------------
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.5/maven-resources-plugin-2.5.pom
 
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.5/maven-resources-plugin-2.5.pom (7 KB at 9.1 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.5/maven-resources-plugin-2.5.jar
 
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.5/maven-resources-plugin-2.5.jar (26 KB at 147.7 KB/sec)
 
[resources:resources]
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-filtering/1.0/maven-filtering-1.0.pom
 
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-filtering/1.0/maven-filtering-1.0.pom (6 KB at 46.9 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-filtering/1.0/maven-filtering-1.0.jar
 
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/shared/maven-filtering/1.0/maven-filtering-1.0.jar (42 KB at 94.2 KB/sec)
[debug] execute contextualize
Using 'UTF-8' encoding to copy filtered resources.
skip non existing resourceDirectory /Users/silenus/NetBeansProjects/helloworld5/src/main/resources
 
[compiler:compile]
Compiling 1 source file to /Users/silenus/NetBeansProjects/helloworld5/target/classes
 
[exec:exec]
Hello World!
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 3.142s
Finished at: Sun Aug 26 20:58:13 CEST 2012
Final Memory: 10M/81M
------------------------------------------------------------------------

Ajout d'une dépendance

Pour illustrer l'utilité de Maven, je vais ajouter une fonctionnalité à mon beau "helloworld5". Je vais le faire parler Xml. Et, pour ce faire, je souhaite utiliser la librairie XStream.

Habituellement, je modifie directement le pom.xml, mais il existe aussi une possibilité via l'interface utilisateur de Netbeans.


Une recherche de XStream me propose directement les librairies qui pourraient m'intéresser.

Et voilà. Un clic et la dépendance est créée et donc, la librairie utilisable.

Projet de test

Container

package ch.conceptforge.helloworld5;
 
public class Container {
 
    private String text;
 
    public Container() {
    }
 
    public Container(String text) {
        this.text = text;
    }
 
    public String getText() {
        return text;
    }
 
    public void setText(String text) {
        this.text = text;
    }    
 
}

App

package ch.conceptforge.helloworld5;
 
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
 
public class App
{
 
    private static Container c;
 
    private static void dispalyText() {
        System.out.println(c.getText());
    }
 
    private static void displayXml() {
        XStream xs = new XStream(new DomDriver());
        xs.alias("container", Container.class);
        System.out.println(xs.toXML(c));
    }
 
    public static void main( String[] args )
    {
        c = new Container("Hello World!");
        dispalyText();
        displayXml();
    }
}

Nouveau run

cd /Users/silenus/NetBeansProjects/helloworld5; JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home "/Applications/NetBeans/NetBeans 7.1.2.app/Contents/Resources/NetBeans/java/maven/bin/mvn" "-Dexec.args=-classpath %classpath ch.conceptforge.helloworld5.App" -Dexec.executable=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java -Dexec.classpathScope=runtime process-classes org.codehaus.mojo:exec-maven-plugin:1.2:exec
Scanning for projects...
 
------------------------------------------------------------------------
Building helloworld5 1.0-SNAPSHOT
------------------------------------------------------------------------
 
[resources:resources]
[debug] execute contextualize
Using 'UTF-8' encoding to copy filtered resources.
Copying 0 resource
 
[compiler:compile]
Compiling 2 source files to /Users/silenus/NetBeansProjects/helloworld5/target/classes
 
[exec:exec]
Hello World!
 
  Hello World!
 
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 1.585s
Finished at: Sun Aug 26 21:38:49 CEST 2012
Final Memory: 8M/81M
------------------------------------------------------------------------

Conclusion

Mon but, dans cet article, n'était pas de réécrire le livre sur Maven, que j'ai présenté au début de l'article. Je ne souhaite pas non plus essayer de parler de Maven dans sa globalité.
Maven est présent dans la plupart des projets Java sur lesquels je travaille. C'est un outil qui fait partie de ma vie de tous les jours et qui me rend de grands services. Dans l'immense majorité des projets Java, j'utilise Netbeans comme EDI. Il y a quelque temps, j'ai eu l'occasion d'utiliser Eclipse pour un projet. Je dois avouer que j'ai été très surpris de voir à quel point Maven à l'air d'une pièce rapportée sous Eclipse par rapport à ce que j'ai l'habitude d'utiliser avec le couple Netbeans/Maven.