Externalized filters when building a Maven project

668 views Asked by At

I really need some advice here...

Here is my situation : I have a main maven project P1 with several resources to filter depending on target environment.

I would like to have my filter files in another project called P2, only made for hosting filter files (for this project and also other projects) in order to manage their configuration independently of P1.

1st question : Are there other people doing this ? Is it a good idea ?

Now my problem is how to filter the P1 resources using P2 filters. In my P1 POM, the use of directory "../P2" for the filter can't be used, because first I dislike this, and second, I use Jenkins for generation, and for Jenkins P1 and P2 are not in the same parent directory.

So I would like to make P1 refers to P2 as a jar dependency. But how to define that a Maven filter is not a file in a directory, but a resource in a jar dependency ? The only way I found for this is to use "Custom Resource Filters" of the Maven Resource Plugin (https://maven.apache.org/plugins/maven-resources-plugin/examples/custom-resource-filters.html), in which I link to a P2 custom class doing the filtering.

This solution was working for war generation through Maven. BUT developers need to use Maven Eclipse WTP functionalities in order to deploy the project on their servers, and to use in particular on-the-fly resource filtering (without needing to run a maven build). And the "Custom Resource Filters" doesn't work with Eclipse WTP on-the-fly resource filtering.

So I don't really know what to do.. What would you advice me ? Or why don't you have this problem ?

Thanks a lot

1

There are 1 answers

0
sch On BEST ANSWER

Well, what i finally did was to keep the P2 project for filters. In my P2 project, I created a class hugely inspired from the default ResourceFilter class.

package com.ads.xx.xx;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.maven.model.Resource;
import org.apache.maven.shared.filtering.DefaultMavenResourcesFiltering;
import org.apache.maven.shared.filtering.MavenFileFilter;
import org.apache.maven.shared.filtering.MavenFilteringException;
import org.apache.maven.shared.filtering.MavenResourcesExecution;
import org.apache.maven.shared.filtering.MavenResourcesFiltering;
import org.apache.maven.shared.utils.PathTool;
import org.apache.maven.shared.utils.ReaderFactory;
import org.apache.maven.shared.utils.io.FileUtils;
import org.apache.maven.shared.utils.io.FileUtils.FilterWrapper;
import org.apache.maven.shared.utils.io.IOUtil;
import org.codehaus.plexus.util.Scanner;
import org.sonatype.plexus.build.incremental.BuildContext;

/**
 * @plexus.component role="org.apache.maven.shared.filtering.MavenResourcesFiltering"
 *                  role-hint="default"
 *
 */

public class ResourceFilterxxxGlobal extends DefaultMavenResourcesFiltering implements MavenResourcesFiltering {



    private static final String[] EMPTY_STRING_ARRAY = { };

    private static final String[] DEFAULT_INCLUDES = { "**/**" };

    private List<String> defaultNonFilteredFileExtensions;

    /**
     * @plexus.requirement
     */
    public BuildContext buildContext;


    /**
     * @plexus.requirement role-hint="default"
     */
    private MavenFileFilter mavenFileFilter;


    public ResourceFilterAtamsGlobal() {
        super();
    }



    public void filterResources(MavenResourcesExecution mavenResourcesExecution)
            throws MavenFilteringException {

        List<String> listeFiltres = new ArrayList<String>();
        String environnement = mavenResourcesExecution.getMavenProject().getProperties().getProperty("environnement");
        String fonctionnel = mavenResourcesExecution.getMavenProject().getProperties().getProperty("fonctionnel");
        String application = mavenResourcesExecution.getMavenProject().getProperties().getProperty("application");

        listeFiltres.add("filters/plateformes/" + environnement + "_technique.properties");
        listeFiltres.add("filters/applications/" + application + "/fonctionnel/" + fonctionnel + ".properties");
        listeFiltres.add("filters/applications/" + application + "/specifique/" + environnement + "_specifique.properties");

        final Properties fileProps = new Properties();
        DataInputStream dis = null;

        try {
            for (String filtrePath : listeFiltres) {
                dis = new DataInputStream(getClass().getClassLoader().getResourceAsStream(filtrePath));
                if (dis == null) {
                    System.out.println("Le fichier " + filtrePath + " n'existe pas.");
                    throw new MavenFilteringException("Le fichier " + filtrePath + " n'existe pas.");
                }
                fileProps.load(dis);
                mavenResourcesExecution.getMavenProject().getProperties().putAll(fileProps);
                dis.close();
            }
        } 
        catch (IOException e) {
            e.printStackTrace();
            throw new MavenFilteringException();
        }
        finally {
            try {
                if (dis != null) {
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NullPointerException e) {
                System.out.println("Le fichier de filtre n'existe pas.");
                throw new MavenFilteringException("Le fichier de filtre n'existe pas.");
            }
        }

        System.out.println("Propriétés trouvées : " + fileProps.entrySet());
        mavenResourcesExecution.getMavenProject().getProperties().putAll(fileProps);


        if ( mavenResourcesExecution == null )
        {
            throw new MavenFilteringException( "mavenResourcesExecution cannot be null" );
        }

        if ( mavenResourcesExecution.getResources() == null )
        {
            getLogger().info( "No resources configured skip copying/filtering" );
            return;
        }

        if ( mavenResourcesExecution.getOutputDirectory() == null )
        {
            throw new MavenFilteringException( "outputDirectory cannot be null" );
        }

        if ( mavenResourcesExecution.isUseDefaultFilterWrappers() )
        {
            List<FileUtils.FilterWrapper> filterWrappers = new ArrayList<FileUtils.FilterWrapper>();
            if ( mavenResourcesExecution.getFilterWrappers() != null )
            {
                filterWrappers.addAll( mavenResourcesExecution.getFilterWrappers() );
            }
            filterWrappers.addAll( mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution ) );
            mavenResourcesExecution.setFilterWrappers( filterWrappers );
        }

        if ( mavenResourcesExecution.getEncoding() == null || mavenResourcesExecution.getEncoding().length() < 1 )
        {
            getLogger().warn( "Using platform encoding (" + ReaderFactory.FILE_ENCODING
                                  + " actually) to copy filtered resources, i.e. build is platform dependent!" );
        }
        else
        {
            getLogger().info(
                "Using '" + mavenResourcesExecution.getEncoding() + "' encoding to copy filtered resources." );
        }

        for ( Resource resource : mavenResourcesExecution.getResources() )
        {

            if ( getLogger().isDebugEnabled() )
            {
                String ls = System.getProperty( "line.separator" );
                StringBuffer debugMessage =
                    new StringBuffer( "resource with targetPath " ).append( resource.getTargetPath() ).append( ls );
                debugMessage.append( "directory " ).append( resource.getDirectory() ).append( ls );
                debugMessage.append( "excludes " ).append( resource.getExcludes() == null ? " empty "
                                                                           : resource.getExcludes().toString() ).append( ls );
                debugMessage.append( "includes " ).append( resource.getIncludes() == null ? " empty "
                                                                           : resource.getIncludes().toString() );
                getLogger().debug( debugMessage.toString() );
            }

            String targetPath = resource.getTargetPath();

            File resourceDirectory = new File( resource.getDirectory() );

            if ( !resourceDirectory.isAbsolute() )
            {
                resourceDirectory =
                    new File( mavenResourcesExecution.getResourcesBaseDirectory(), resourceDirectory.getPath() );
            }

            if ( !resourceDirectory.exists() )
            {
                getLogger().info( "skip non existing resourceDirectory " + resourceDirectory.getPath() );
                continue;
            }

            // this part is required in case the user specified "../something" as destination
            // see MNG-1345
            File outputDirectory = mavenResourcesExecution.getOutputDirectory();
            boolean outputExists = outputDirectory.exists();
            if ( !outputExists && !outputDirectory.mkdirs() )
            {
                throw new MavenFilteringException( "Cannot create resource output directory: " + outputDirectory );
            }

            boolean ignoreDelta = !outputExists || buildContext.hasDelta( mavenResourcesExecution.getFileFilters() )
                || buildContext.hasDelta( getRelativeOutputDirectory( mavenResourcesExecution ) );
            getLogger().debug( "ignoreDelta " + ignoreDelta );
            Scanner scanner = buildContext.newScanner( resourceDirectory, ignoreDelta );

            setupScanner( resource, scanner );

            scanner.scan();

            if ( mavenResourcesExecution.isIncludeEmptyDirs() )
            {
                try
                {
                    File targetDirectory =
                        targetPath == null ? outputDirectory : new File( outputDirectory, targetPath );
                    copyDirectoryLayout( resourceDirectory, targetDirectory, scanner );
                }
                catch ( IOException e )
                {
                    throw new MavenFilteringException(
                        "Cannot copy directory structure from " + resourceDirectory.getPath() + " to "
                            + outputDirectory.getPath() );
                }
            }

            List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );

            getLogger().info(
                "Copying " + includedFiles.size() + " resource" + ( includedFiles.size() > 1 ? "s" : "" ) + (
                    targetPath == null ? "" : " to " + targetPath ) );

            for ( String name : includedFiles )
            {

                File source = new File( resourceDirectory, name );

                File destinationFile =
                    getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );

                boolean filteredExt =
                    filteredFileExtension( source.getName(), mavenResourcesExecution.getNonFilteredFileExtensions() );

                mavenFileFilter.copyFile( source, destinationFile, resource.isFiltering() && filteredExt,
                                          mavenResourcesExecution.getFilterWrappers(),
                                          mavenResourcesExecution.getEncoding(),
                                          mavenResourcesExecution.isOverwrite() );
            }

            // deal with deleted source files

            scanner = buildContext.newDeleteScanner( resourceDirectory );

            setupScanner( resource, scanner );

            scanner.scan();

            List<String> deletedFiles = Arrays.asList( scanner.getIncludedFiles() );

            for ( String name : deletedFiles )
            {
                File destinationFile =
                    getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );

                destinationFile.delete();

                buildContext.refresh( destinationFile );
            }

        }

    }

    private File getDestinationFile( File outputDirectory, String targetPath, String name, MavenResourcesExecution mavenResourcesExecution )
        throws MavenFilteringException
    {
        String destination = name;

        if ( mavenResourcesExecution.isFilterFilenames() && mavenResourcesExecution.getFilterWrappers().size() > 0 )
        {
            destination = filterFileName( destination, mavenResourcesExecution.getFilterWrappers() );
        }

        if ( targetPath != null )
        {
            destination = targetPath + "/" + destination;
        }

        File destinationFile = new File( destination );
        if ( !destinationFile.isAbsolute() )
        {
            destinationFile = new File( outputDirectory, destination );
        }

        if ( !destinationFile.getParentFile().exists() )
        {
            destinationFile.getParentFile().mkdirs();
        }
        return destinationFile;
    }

    private String[] setupScanner( Resource resource, Scanner scanner )
    {
        String[] includes = null;
        if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
        {
            includes = (String[]) resource.getIncludes().toArray( EMPTY_STRING_ARRAY );
        }
        else
        {
            includes = DEFAULT_INCLUDES;
        }
        scanner.setIncludes( includes );

        String[] excludes = null;
        if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
        {
            excludes = (String[]) resource.getExcludes().toArray( EMPTY_STRING_ARRAY );
            scanner.setExcludes( excludes );
        }

        scanner.addDefaultExcludes();
        return includes;
    }

    private void copyDirectoryLayout( File sourceDirectory, File destinationDirectory, Scanner scanner )
        throws IOException
    {
        if ( sourceDirectory == null )
        {
            throw new IOException( "source directory can't be null." );
        }

        if ( destinationDirectory == null )
        {
            throw new IOException( "destination directory can't be null." );
        }

        if ( sourceDirectory.equals( destinationDirectory ) )
        {
            throw new IOException( "source and destination are the same directory." );
        }

        if ( !sourceDirectory.exists() )
        {
            throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
        }

        List<String> includedDirectories = Arrays.asList( scanner.getIncludedDirectories() );

        for ( String name : includedDirectories )
        {
            File source = new File( sourceDirectory, name );

            if ( source.equals( sourceDirectory ) )
            {
                continue;
            }

            File destination = new File( destinationDirectory, name );
            destination.mkdirs();
        }
    }

    private String getRelativeOutputDirectory( MavenResourcesExecution execution )
    {
        String relOutDir = execution.getOutputDirectory().getAbsolutePath();

        if ( execution.getMavenProject() != null && execution.getMavenProject().getBasedir() != null )
        {
            String basedir = execution.getMavenProject().getBasedir().getAbsolutePath();
            relOutDir = PathTool.getRelativeFilePath( basedir, relOutDir );
            if ( relOutDir == null )
            {
                relOutDir = execution.getOutputDirectory().getPath();
            }
            else
            {
                relOutDir = relOutDir.replace( '\\', '/' );
            }
        }

        return relOutDir;
    }

    /*
     * Filter the name of a file using the same mechanism for filtering the content of the file.
     */
    private String filterFileName( String name, List<FilterWrapper> wrappers )
        throws MavenFilteringException
    {

        Reader reader = new StringReader( name );
        for ( FilterWrapper wrapper : wrappers )
        {
            reader = wrapper.getReader( reader );
        }

        StringWriter writer = new StringWriter();

        try
        {
            IOUtil.copy( reader, writer );
        }
        catch ( IOException e )
        {
            throw new MavenFilteringException( "Failed filtering filename" + name, e );
        }

        String filteredFilename = writer.toString();

        if ( getLogger().isDebugEnabled() )
        {
            getLogger().debug( "renaming filename " + name + " to " + filteredFilename );
        }
        return filteredFilename;
    }
}

And then, in the P1 project, I call it through :

<profile>
        <id>local</id>
        <activation>
            <file>
                <exists>../Apl/pom.xml</exists>
            </file>
        </activation>
        <build>
            <filters>
                <filter>../Apl/src/main/resources/filters/plateformes/${environnement}_technique.properties</filter>
                <filter>../Apl/src/main/resources/filters/applications/${application}/specifique/${environnement}_specifique.properties</filter>
                <filter>../Apl/src/main/resources/filters/applications/${application}/fonctionnel/${fonctionnel}.properties</filter>
            </filters>
        </build>
    </profile>   

    <profile>
        <id>serveur</id>
        <activation>
            <file>
                <missing>../Apl/pom.xml</missing>
            </file>
        </activation>
        <build>
            <plugins>
            <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.7</version>
        <dependencies>
            <dependency>
                <groupId>com.ads.xxx</groupId>
                <artifactId>apl</artifactId>
                <version>1.0.1-SNAPSHOT</version>
            </dependency>
        </dependencies>

        </plugin>
        </plugins>
        </build>

    </profile>

Local profile is for developer, while Server profile is to deploy on remote servers through Jenkins. It works quite well.