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>