vendredi 21 février 2014

Les VarInts

Si vous êtes ammené à utiliser ou étudier le protocole Protobuf de Google, vous y découvrirez un élément particulièrement intéressant, les VarInts.

Ce format de stockage peut être intéressant à mettre en oeuvre. Il est simple et présente des avantages en terme de volumétrie.
Imaginons que les valeurs suivantes pour un même champs et leur équivalent binaire:
  • 50 : 0110010
  • 322 : 00000001 11000010
  • 70000 : 00000001 00010001 01110000
On constate que pour un même champs, les valeurs prennent des tailles différentes.
Si l'on souhaite stocker jusquà la valeur 70000, il faut alors 24 bits.

Admettons que majoritairement, les valeurs du champs soient des nombres entre 0 et 65535, soit entre 1 et 2 octets, avec quelques valeurs sur 3 octets.
Il est dommage de perdre des octets au niveau place, surtout si comme pour une trame réseau, cela devenir gênant au niveau volume.

L'idée des VarInts est la même que UTF-8.
Le bit de poid fort (le 8ème bit) va indiqué s'il le nombre est stocké aussi sur l'octet suivant. Exemple :
Pour la valeur 50, la valeur binaire est 0 110010. L'octet de poid fort est à zéro, donc, le chiffre est stocké sur 1 octet.
Pour la valeur 322, soit 0000000101000010 en binaire, nous alons découper la valeur binaire en 7 bits :
0000000101000010
----------------
00
0000010
1000010
Une fois cette opération fait, est ajouter 1 ou 0 suivant si il y a une suite. Dans l'exemple, cela donne :
00
00000010
11000010
Ce qui donne une valeur fausse de 706.
Le traitement est identique la valeur 70000.

Il n'est pas possible de stocker les valeurs comme ça sur un disque, car il n'est pas possible en l'état de traiter correctement les valeurs.
Il est nécesaire d'inverser l'ordre des octets. Ainsi, sur disque sera écrit la valeur 11000010 puis la valeur 00000010.
De ce fait, une lecture séquentielle est possible. Voici l'algorithme :
Lire le premier octet.
Si bit de poids fort est 1
  Mettre bit de poid fort à 0
  Lecture deuxième octet
  Initialiser un nombre 16 bits à 0
  Mettre le deuxième octets dedans et décaler 7 bits vers la gauche
  Faire un ou logique avec le premier octet modifié
Fin Si
Le traitement des nombres signés est identique.
L'inconvénient majeur qui apparait, c'est le temps de traitement, plus coûteux qu'une simple lecture.
Deuxième inconvénient, c'est que le nombre 255, au lieu d'être enrigistré sur 1 octet, le sera sur 2.
La mise en place des VarInts est donc un choix à étudier précisément suivant les cas.

vendredi 1 novembre 2013

Tester ses plugins Maven

Lorsqu'il y a développement, il y a généralement test.
Mais tester un plugin (Mojo) Maven n'est pas évident. Il existe plusieurs solutions, plus ou moins pratique.
1 - Tester sans framework dédié.
Pour tester un mojo Maven (une classe Java associée à un goal), le plus simple consiste a déclarer tous les attributs exposés à Maven en protected ou en package private.
Par exemple, un Mojo affichant une chaine de caractère.
public class ShowMessageMojo extends AbstractMojo {
 /**
  * @parameter
  * @required
  */
 private String message ;
    
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info(message) ;
    }
}

Pour tester ce mojo, il est nécessaire de changer private String message ; en protected String message ;. Ainsi, il est possible de créer un classe de test qui hérite de ce mojo et qui ajoutera un getter/setter.
Il est aussi tout a fait possible, dans le mojo du départ de mettre des getter/setter. Ces getter/setter seront en plus utilisé par Maven pour modifier l'attribut.

Ensuite, une simple classe JUnit permet de tester.
Le gros inconvénient viendra lorsque le mojo voudra récupérer le projet Maven, les repositories...
2 - Tester avec maven-plugin-testing-harness
Pour pouvoir exécuter un mojo dans un contexte Maven, il existe le plugin maven-plugin-testing-harness.
Dans l'exemple suivant, voici la dépendance utilisée :
<dependency>
  <groupId>org.apache.maven.plugin-testing</groupId>
  <artifactId>maven-plugin-testing-harness</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>

Reprise de l'exemple précédent, mais avec l'ajout du projet maven :
/**
 * @goal showMessage
 * @requiresProject true
 * 
 * @author Emeric MARTINEAU
 */
public class ShowMessageMojo extends AbstractMojo {
 /**
  * Le POM.
  * 
  * @parameter expression="${project}"
  * @required
  */
 protected MavenProject project;
 
 /**
  * @parameter
  * @required
  */
 private String message ;
    
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info(project.getBasedir()) ;
    }
}

Comment alors créer un projet maven dans le cadre d'un test ?
Pour cela, une classe de test dérivant de AbstractMojoTestCase doit être créée.

Cette classe met a disposition un ensemble de méthode qui facilite le test.

Il faut au préalable, créer un fichier pom.xml qui appel le plugin, exemple :
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>group-id-bidon</groupId>
    <artifactId>artifact-id-bidon</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Test plugin</name>

    <build>
        <plugins>
            <plugin>
                <groupId>org.mycompany.plugin</groupId>
                <artifactId>maven-toto-plugin</artifactId>
                <version>1.0.0-SNAPSHOT</version>
                <configuration>
                    <message>SRVF_TestMaven</message>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Ce fichier de pom permet de récupérer une instance du plugin/mojo.
Voici comment instancier le mojo et l'exécuter :
// Par convention, le classe de test porte le nom de la
// classe du mojo, suffixé avec "Test".
public class ShowMessageMojoTest extends AbstractMojoTestCase {
    /**
     * @throws Exception if any
     */
    public void testMojo() throws Exception
    {
        // Lecture du fichier pom pour initiliser le context Plexus
        File pom = getTestFile( "src/test/resources/pom.xml" );
        assertNotNull( pom );
        assertTrue( pom.exists() ) ;
        
        // Recuperation du mojo par le nom du goal
        ShowMessageMojo myMojo = (ShowMessageMojo) lookupMojo( "showMessage", pom );
        assertNotNull( myMojo );
                
        // Execution du mojo
        myMojo.execute();
    }
}

Le résultat ?
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.469 sec <<< FAILURE!
testMojo(ShowMessageMojoTest)  Time elapsed: 0.283 sec  <<< ERROR!
java.lang.NullPointerException: null
 at ShowMessageMojo.execute(ShowMessageMojo.java:31)
 at ShowMessageMojoTest.testMojo(ShowMessageMojoTest.java:30)

NullPointerException à la ligne getLog().info(project.getBaseDir()) ;. Pourquoi ?
Parce que expression="${project}" n'est pas interprété.

Il faudra donc, manuellement affecter une valeur, juste avant l'exécution du mojo, avec :
public class ShowMessageMojoTest extends AbstractMojoTestCase {
    /**
     * @throws Exception if any
     */
    public void testMojo() throws Exception
    {
        // Lecture du fichier pom pour initiliser le context Plexus
        File pom = getTestFile( "src/test/resources/pom.xml" );
        assertNotNull( pom );
        assertTrue( pom.exists() ) ;

        // Recuperation du mojo par le nom du goal
        ShowMessageMojo myMojo = (ShowMessageMojo) lookupMojo( "showMessage", pom );
        assertNotNull( myMojo );

        // Creation du projet maven
        final MavenProject mvnProject = new MavenProject() ;
        mvnProject.setBasedir(new File("/")) ;
        setVariableValueToObject(myMojo, "project", mvnProject) ;
  
        // Execution du mojo
        myMojo.execute();
    }
}

Il en sera ainsi pour toute les proriétés.

Evidement, cela reste encore un peu manuel. Mais d'autre méthode existe pour configurer le mojo.

Il est possible d'utiliser la ligne configureMojo(myMojo, "maven-toto-plugin", pom) ;.
public class ShowMessageMojoTest extends AbstractMojoTestCase {
    /**
     * @throws Exception if any
     */
    public void testMojo() throws Exception
    {
        // Lecture du fichier pom pour initiliser le context Plexus
        File pom = getTestFile( "src/test/resources/pom.xml" );
        assertNotNull( pom );
        assertTrue( pom.exists() ) ;

        // Recuperation du mojo par le nom du goal
        ShowMessageMojo myMojo = (ShowMessageMojo) lookupMojo( "showMessage", pom );
        assertNotNull( myMojo );

        configureMojo(myMojo, "maven-toto-plugin", pom) ;
  
        // Execution du mojo
        myMojo.execute();
    }
}

Le seul inconvénient de ce système, c'est qu'il ne gère pas la possiblilité d'avoir dans le même fichier de configuration, plusieurs goal avec la baslise <execution />.
Il faut donc pour chaque goal créer un pom.

jeudi 10 octobre 2013

La généricité en Java : utilisation particulière

Les génériques en Java permettent une grande souplesses dans le code.

Il peut très intéressant de tirer partie de cette généricité pour par exemple, automtiser la création d'objet.

Si une classe attend un objet en entrée et retourne un autre objet, il peut être pratique d'automatisé la création d'objet.

Voici un exemple de classe :
import org.apache.commons.beanutils.ConstructorUtils;
import java.lang.reflect.ParameterizedType;

public abstract class Services<Q extends Query, R extends Response> {
    private Q q ;
    private R r ;

    public Services() {
        q = (Q) buildGeneric(getClass(), 0) ;
        r = (R) buildGeneric(getClass(), 1) ;
    }
    
    public abstract void execute() ;
    
    public Q getQuery() {
        return q ;
    }

    public R getResponse() {
        return r ;
    }

    private static Object build(Class<?> clazz) {
        Object result = null;
        try {
            result = ConstructorUtils.invokeConstructor(clazz, null);
        } catch (Exception e) {
            // Nothing
        }
        return result;
    }

    private static Object buildGeneric(Class<?> clazz, int position) {
        return build(getClassGeneric(clazz, position));
    }

    private static Class<?> getClassGeneric(Class<?> classGeneric, int position) {
        return (Class<?>) ((ParameterizedType) classGeneric.getGenericSuperclass()).getActualTypeArguments()[position];
    }    
}

Cette classe Services créé automatiquement les objets de requête et de réponse en récupérant dans la classe concrète les paramètres de générique.
Ces objets peuvent être de n'importe quel type.

Le constructeur récupère le nom de la classe des deux génériques et créer les objets (en utilisant une classe externe ConstructorUtils de common-beanutil pour plus de facilité).

Chaque argument générique est attaché à la classe lors de la compilation.
Cela signifie qu'il faut créer une classe à la main.
Il n'est pas possible de créer une classe anonyme à l'éxécution.

Voici une classe héritant de la classe Services :
public class MonService extends Services<MaQuery, MaResponse> {
    @Override
    public void execute() {
        System.out.println("TestSevice : execute()");
        
    }
}

Les deux classes de requête et de réponse :
public class MaQuery {
    @Override
    public String toString() {
        return "MaQuery" ;
    }
}

public class MaResponse {
    @Override
    public String toString() {
        return "MaResponse" ;
    }

}

Maintenant, une classe de test :
public class TestService {
    public static void main(String[] args) {
        Services s = new MonService() ;
        
        s.execute() ;
        
        System.out.println(s.getQuery().toString()) ;
        System.out.println(s.getResponse().toString()) ;
    }
}

Et voilà. Cela permet un code simple, générique, clair.
Il pourrait être ajouter des contrôles commun sur les objets requête et réponse.

Les deux défauts, comme dit précédemment, il faut créer une classe à la main pour qu'elle soit compilée afin d'avoir les informations de généricité (nom classe requête et réponse) via les meta-data.
La performance. Avec ce système, la réflexion est mise en jeu et coûte cher en temps d'exécution.

Toutefois, cette méthode est souvent utilisée. Pour pallier le problème de performance, d'autre mécanisme sont mis en place (cache...)

vendredi 13 septembre 2013

Ajouter un filtre personnalisé au plugin maven resource

Suite au post précédent post Multi encoding avec le plugin resource de Maven, il existe un autre moyen de personnaliser le filtering maven.
Il est possible soit de le substituer, soit de le compléter (ce qui répond aux deux bug maven ressource : https://jira.codehaus.org/browse/MRESOURCES-118 et https://jira.codehaus.org/browse/MRESOURCES-31).

En effet, depuis le version 2.4 du plugin resource, il est possible de spécifier un mavenFilteringHints.
A ce sujet, le site met à disposition la documentation suivante : http://maven.apache.org/plugins/maven-resources-plugin/examples/custom-resource-filters.html
Toutefois, l'exemple est peu parlant.
Nous allons donc voir ici comment créer un filtering personnaliser.

Dans un premier temps, il est nécessaire de créer un projet Maven de type JAR.
Le pom doit être renseigné comme ci-dessous :
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>fr.emeric.filtering</groupId>
  <artifactId>myFiltering</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>myFiltering Maven Mojo</name>

  <dependencies>
    <dependency>
      <groupId>org.apache.maven.shared</groupId>
      <artifactId>maven-filtering</artifactId>
      <version>1.1</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.plexus</groupId>
        <artifactId>plexus-maven-plugin</artifactId>
        <version>1.3.4</version>
        <executions>
          <execution>
            <goals>
              <goal>descriptor</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

La dépendance maven-filtering est là pour importer l'interface org.apache.maven.shared.filtering.MavenResourcesFiltering et toutes les autres classes nécessaires.
Le plugin plexus-maven-plugin est là lui pour générer les méta-données nécessaire à plexus.

Ensuite, il suffit de créer la classe ci-dessous :
package fr.emeric.filtering.myFiltering;

import java.io.File;
import java.util.Collections;
import java.util.List;

import org.apache.maven.model.Dependency;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Resource;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.filtering.MavenFilteringException;
import org.apache.maven.shared.filtering.MavenResourcesExecution;
import org.apache.maven.shared.filtering.MavenResourcesFiltering;
import org.codehaus.plexus.util.FileUtils.FilterWrapper;

/**
 * @plexus.component role="org.apache.maven.shared.filtering.MavenResourcesFiltering" 
 *                   role-hint="myFilter"
 */
public class MyFiltering
    implements MavenResourcesFiltering
{

  public void filterResources(List resources, File outputDirectory,
      MavenProject mavenProject, String encoding,
      List fileFilters, List nonFilteredFileExtensions,
      MavenSession mavenSession) throws MavenFilteringException {    
  }

  public void filterResources(List resources, File outputDirectory,
      String encoding, List filterWrappers,
      File resourcesBaseDirectory, List nonFilteredFileExtensions)
      throws MavenFilteringException {
    
  }

  public List getDefaultNonFilteredFileExtensions() {
    return Collections.EMPTY_LIST;
  }

  public boolean filteredFileExtension(String fileName,
      List userNonFilteredFileExtensions) {
    return true;
  }

  public void filterResources(MavenResourcesExecution mavenResourcesExecution)
      throws MavenFilteringException {
    System.out.println("Hello depuis mon filtering !");
    
    List l = mavenResourcesExecution.getMavenProject().getDependencies() ;
    
    for(Dependency da : l) {
      System.out.println(da.toString());
    }
    
  }    
}

La méthode public void filterResources(MavenResourcesExecution mavenResourcesExecution) est applelée pour faire le filtre.

Il faut bien garder à l'esprit que le filtre personnalisé est appelé après le filtre par défaut de Maven.
C'est à dire que les fichiers ressources sont déjà filtrés quand le code est appelé.
Cela permet donc de rajouter des filtres.
Si vous voulez remplacer le filtre par défaut, il n'y a aucun problème. En effet, l'objet MavenResourcesExecution mavenResourcesExecution est le même que celui donné au filtre par défaut. Il y aura juste deux filtres exécutés, le premier ne servant à rien.

L'exemple ci-dessus montre que le filtre ne fait rien. Il affiche un texte Hello depuis mon filtering ! et la liste des dépendances.

Maintenant que notre classe de filtre est créer, le jar contenant cette classe doit être générée et installée dans le repository maven (pour cette exemple, le repository local est suffisant) :
mvn clean install

A présent, dans un autre projet, où l'on souhaite utiliser ce filtre personnalisé, le plugin maven resource doit être configuré :
<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          ...
          <dependencies>
            <dependency>
              <groupId>fr.emeric.filtering</groupId>
              <artifactId>myFiltering</artifactId>
              <version>0.0.1-SNAPSHOT </version>
            </dependency>
          </dependencies>
    <mavenFilteringHints>
            <mavenFilteringHint>myFilter</mavenFilteringHint>
          </mavenFilteringHints>
          ...
        </configuration>
      </plugin>
    </plugins>
    ...
  </build>
  ...
</project>

Le jar produit avec le filtre personnalisé est mis en dépendance du plugin maven resource.
Dans le paramètre mavenFilteringHint, le nom indiqué correspond à l'indication de la java doc role-hint="myFilter".
Voici le résultat lorsque maven s'exécute :
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
Hello depuis mon filtering !
Dependency {groupId=org.apache.maven, artifactId=maven-plugin-api, version=2.0.6, type=jar}
Dependency {groupId=org.apache.maven, artifactId=maven-model, version=3.0.5, type=jar}

lundi 9 septembre 2013

Multi encoding avec le plugin resource de Maven

Par défaut, avec le plugin resource de Maven, il n'est pas possible de spécifier un encodage spécifique par ressource.
Dans la balise resources, il est uniquement possible de spécifier un seul encodage.

Il est toutefois possible de contourner le problème même si cela peut sembler un peu fastidieux.

Le plugin resources à un goal copy-resources qui permet de spécifier des ressources supplémentaires avec d'autres paramètres.
C'est là que se situe l'astuce.
Dans la balise générale resources, toutes les resources sont exclues.
Ensuite, "manuellement", les plugins resources avec le goal copy-resources est lancé avec un configuration différente pour chaque encodage.
Ainsi, un répertoire UFT8 contient les ressources avec cet encodage, idem avec le répertoire cp1252.
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
        <excludes>
          <exclude>**/**</exclude>
        </excludes>
    </resource>
  </resources>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-resources-plugin</artifactId>
      <version>2.6</version>
      <executions>
        <execution>
          <id>copy-resources1</id>
          <phase>process-resources</phase>
          <goals>
            <goal>copy-resources</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
            <encoding>UTF-8</encoding>
            <resources>
              <resource>
                <directory>src/main/resources/UTF8</directory>
                <filtering>true</filtering>
              </resource>
            </resources>
          </configuration>
        </execution>
        <execution>
          <id>copy-resources2</id>
          <phase>process-resources</phase>
          <goals>
            <goal>copy-resources</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
            <encoding>CP1252</encoding>
            <resources>
              <resource>
                <directory>src/main/resources/cp1252</directory>
                <filtering>true</filtering>
              </resource>
            </resources>
          </configuration>
        </execution>
      </executions>
    </plugin>
<build>

lundi 26 août 2013

Fichier de propriétés Maven mal encodé. [2/2]

Pourquoi donc, lorsque le fichier est chargé l'encodage n'est pas bon ?

Si on reprend le code et que l'on va plus loin :
public static Properties loadPropertyFile( File propFile, Properties baseProps )
    throws IOException
{
    if ( !propFile.exists() )
    {
        throw new FileNotFoundException( propFile.toString() );
    }
    final Properties fileProps = new Properties();
    final FileInputStream inStream = new FileInputStream( propFile );
    try {
        fileProps.load(inStream) ;
    }
 ....

On constate que c'est la classe java.util.Properties qui charge les fichiers de propriétés.
Or dans la java doc (http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html) on lit :
"except the input/output stream is encoded in ISO 8859-1 character encoding"

"The native2ascii tool can be used to convert property files to and from other character encodings."

Donc, la classe java.util.Properties ne gère que l'ISO 8859-1.
Pour palier ce problème, depuis java 1.6, il existe une méthode public synchronized void load(Reader reader) throws IOException qui prend un objet type java.io.BufferedReader qui couplé avec java.io.InputStreamReader permet le support d'encodage différent.
Cet encodage différent peut soit être configuré via le constructeur, soit via -Dfile.encoding=UTF8.

lundi 12 août 2013

Fichier de propriétés Maven mal encodé. [1/2]

Avec Maven, il est possible de spécifier un fichier de propriétés qui sera utilisé par la suite pour faire du filtrage de fichiers.
Ce fichier, contient la liste des variables à valoriser.

Par exemple, si dans le fichier local.properties, il y a :
titre=En cours de création

Dans le fichier à substituer label.properties, le contenu est le suivant :
monLabel=${titre}

Les deux fichiers sont encodés en UTF8.
Dans le pom.xml, l'encodage est spécifié par :
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
</properties>

Une fois substitué, le résultat du fichier est :
En cours de création

Il y a donc un problème d'encodage mais où ?

Pour faire simple, le filtering jusqu'à Maven 3.0.x en tout cas se fait via la librairie "maven-filtering".
Et si on regarde le code, classe PropertyUtils méthode loadPropertyFile (c'est cette méthode qui est appelé pour le filtering), on a :
public static Properties loadPropertyFile( File propFile, Properties baseProps )
    throws IOException
{
    if ( !propFile.exists() )
    {
        throw new FileNotFoundException( propFile.toString() );
    }
    final Properties fileProps = new Properties();
    final FileInputStream inStream = new FileInputStream( propFile );
    ....

Il n'est pas prévu de pouvoir spécifier un encodage en particulier pour les fichiers contenant les variables qui devront être filtrés.
Le fichier est ouvert avec l'encodage par défaut de la JVM.

Le soucis, est que si lors du lancement de Maven, on ajoute l'option de la JVM -Dfile.encoding=UTF8 cela ne fonctionne pas.
Pourquoi ?
J'y reviendrais dans un autre billet.

Ticket sur le plugin maven ressource sur ce sujet : https://jira.codehaus.org/browse/MRESOURCES-175