View Javadoc
1   package org.apache.maven.shared.filtering;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *    http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.Reader;
25  import java.io.StringReader;
26  import java.io.StringWriter;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.List;
30  
31  import org.apache.maven.model.Resource;
32  import org.apache.maven.shared.utils.PathTool;
33  import org.apache.maven.shared.utils.ReaderFactory;
34  import org.apache.maven.shared.utils.StringUtils;
35  import org.apache.maven.shared.utils.io.FileUtils;
36  import org.apache.maven.shared.utils.io.FileUtils.FilterWrapper;
37  import org.apache.maven.shared.utils.io.IOUtil;
38  import org.codehaus.plexus.component.annotations.Component;
39  import org.codehaus.plexus.component.annotations.Requirement;
40  import org.codehaus.plexus.logging.AbstractLogEnabled;
41  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
42  import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
43  import org.codehaus.plexus.util.Scanner;
44  import org.sonatype.plexus.build.incremental.BuildContext;
45  
46  /**
47   * @author Olivier Lamy
48   */
49  @Component( role = MavenResourcesFiltering.class, hint = "default" )
50  public class DefaultMavenResourcesFiltering
51      extends AbstractLogEnabled
52      implements MavenResourcesFiltering, Initializable
53  {
54  
55      private static final String[] EMPTY_STRING_ARRAY = {};
56  
57      private static final String[] DEFAULT_INCLUDES = { "**/**" };
58  
59      private List<String> defaultNonFilteredFileExtensions;
60  
61      @Requirement
62      private BuildContext buildContext;
63  
64      @Requirement
65      private MavenFileFilter mavenFileFilter;
66  
67      // ------------------------------------------------
68      // Plexus lifecycle
69      // ------------------------------------------------
70      /** {@inheritDoc} */
71      public void initialize()
72          throws InitializationException
73      {
74          // jpg,jpeg,gif,bmp,png
75          this.defaultNonFilteredFileExtensions = new ArrayList<String>( 5 );
76          this.defaultNonFilteredFileExtensions.add( "jpg" );
77          this.defaultNonFilteredFileExtensions.add( "jpeg" );
78          this.defaultNonFilteredFileExtensions.add( "gif" );
79          this.defaultNonFilteredFileExtensions.add( "bmp" );
80          this.defaultNonFilteredFileExtensions.add( "png" );
81      }
82  
83      /** {@inheritDoc} */
84      public boolean filteredFileExtension( String fileName, List<String> userNonFilteredFileExtensions )
85      {
86          List<String> nonFilteredFileExtensions = new ArrayList<String>( getDefaultNonFilteredFileExtensions() );
87          if ( userNonFilteredFileExtensions != null )
88          {
89              nonFilteredFileExtensions.addAll( userNonFilteredFileExtensions );
90          }
91          boolean filteredFileExtension =
92              !nonFilteredFileExtensions.contains( StringUtils.lowerCase( FileUtils.extension( fileName ) ) );
93          if ( getLogger().isDebugEnabled() )
94          {
95              getLogger().debug( "file " + fileName + " has a" + ( filteredFileExtension ? " " : " non " )
96                  + "filtered file extension" );
97          }
98          return filteredFileExtension;
99      }
100 
101     /** {@inheritDoc} */
102     public List<String> getDefaultNonFilteredFileExtensions()
103     {
104         if ( this.defaultNonFilteredFileExtensions == null )
105         {
106             this.defaultNonFilteredFileExtensions = new ArrayList<String>();
107         }
108         return this.defaultNonFilteredFileExtensions;
109     }
110 
111     /** {@inheritDoc} */
112     public void filterResources( MavenResourcesExecution mavenResourcesExecution )
113         throws MavenFilteringException
114     {
115         if ( mavenResourcesExecution == null )
116         {
117             throw new MavenFilteringException( "mavenResourcesExecution cannot be null" );
118         }
119 
120         if ( mavenResourcesExecution.getResources() == null )
121         {
122             getLogger().info( "No resources configured skip copying/filtering" );
123             return;
124         }
125 
126         if ( mavenResourcesExecution.getOutputDirectory() == null )
127         {
128             throw new MavenFilteringException( "outputDirectory cannot be null" );
129         }
130 
131         if ( mavenResourcesExecution.isUseDefaultFilterWrappers() )
132         {
133             List<FileUtils.FilterWrapper> filterWrappers = new ArrayList<FileUtils.FilterWrapper>();
134             if ( mavenResourcesExecution.getFilterWrappers() != null )
135             {
136                 filterWrappers.addAll( mavenResourcesExecution.getFilterWrappers() );
137             }
138             filterWrappers.addAll( mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution ) );
139             mavenResourcesExecution.setFilterWrappers( filterWrappers );
140         }
141 
142         if ( mavenResourcesExecution.getEncoding() == null || mavenResourcesExecution.getEncoding().length() < 1 )
143         {
144             getLogger().warn( "Using platform encoding (" + ReaderFactory.FILE_ENCODING
145                 + " actually) to copy filtered resources, i.e. build is platform dependent!" );
146         }
147         else
148         {
149             getLogger().info( "Using '" + mavenResourcesExecution.getEncoding()
150                 + "' encoding to copy filtered resources." );
151         }
152 
153         for ( Resource resource : mavenResourcesExecution.getResources() )
154         {
155 
156             if ( getLogger().isDebugEnabled() )
157             {
158                 String ls = System.getProperty( "line.separator" );
159                 StringBuilder debugMessage =
160                     new StringBuilder( "resource with targetPath " ).append( resource.getTargetPath() ).append( ls );
161                 debugMessage.append( "directory " ).append( resource.getDirectory() ).append( ls );
162 
163                 // @formatter:off
164                 debugMessage.append( "excludes " ).append( resource.getExcludes() == null ? " empty "
165                                 : resource.getExcludes().toString() ).append( ls );
166                 debugMessage.append( "includes " ).append( resource.getIncludes() == null ? " empty "
167                                 : resource.getIncludes().toString() );
168 
169                 // @formatter:on
170                 getLogger().info( debugMessage.toString() );
171             }
172 
173             String targetPath = resource.getTargetPath();
174 
175             File resourceDirectory = new File( resource.getDirectory() );
176 
177             if ( !resourceDirectory.isAbsolute() )
178             {
179                 resourceDirectory =
180                     new File( mavenResourcesExecution.getResourcesBaseDirectory(), resourceDirectory.getPath() );
181             }
182 
183             if ( !resourceDirectory.exists() )
184             {
185                 getLogger().info( "skip non existing resourceDirectory " + resourceDirectory.getPath() );
186                 continue;
187             }
188 
189             // this part is required in case the user specified "../something" as destination
190             // see MNG-1345
191             File outputDirectory = mavenResourcesExecution.getOutputDirectory();
192             boolean outputExists = outputDirectory.exists();
193             if ( !outputExists && !outputDirectory.mkdirs() )
194             {
195                 throw new MavenFilteringException( "Cannot create resource output directory: " + outputDirectory );
196             }
197 
198             boolean ignoreDelta = !outputExists || buildContext.hasDelta( mavenResourcesExecution.getFileFilters() )
199                 || buildContext.hasDelta( getRelativeOutputDirectory( mavenResourcesExecution ) );
200             getLogger().info( "ignoreDelta " + ignoreDelta );
201             Scanner scanner = buildContext.newScanner( resourceDirectory, ignoreDelta );
202 
203             setupScanner( resource, scanner, mavenResourcesExecution.isAddDefaultExcludes() );
204 
205             scanner.scan();
206 
207             if ( mavenResourcesExecution.isIncludeEmptyDirs() )
208             {
209                 try
210                 {
211                     File targetDirectory =
212                         targetPath == null ? outputDirectory : new File( outputDirectory, targetPath );
213                     copyDirectoryLayout( resourceDirectory, targetDirectory, scanner );
214                 }
215                 catch ( IOException e )
216                 {
217                     throw new MavenFilteringException( "Cannot copy directory structure from "
218                         + resourceDirectory.getPath() + " to " + outputDirectory.getPath() );
219                 }
220             }
221 
222             List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
223 
224             getLogger().info( "Copying " + includedFiles.size() + " resource" + ( includedFiles.size() > 1 ? "s" : "" )
225                 + ( targetPath == null ? "" : " to " + targetPath ) );
226 
227             for ( String name : includedFiles )
228             {
229 
230                 getLogger().info( "Copying file " + name );
231                 File source = new File( resourceDirectory, name );
232 
233                 File destinationFile = getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );
234 
235                 boolean filteredExt =
236                     filteredFileExtension( source.getName(), mavenResourcesExecution.getNonFilteredFileExtensions() );
237 
238                 mavenFileFilter.copyFile( source, destinationFile, resource.isFiltering() && filteredExt,
239                                           mavenResourcesExecution.getFilterWrappers(),
240                                           mavenResourcesExecution.getEncoding(),
241                                           mavenResourcesExecution.isOverwrite() );
242             }
243 
244             // deal with deleted source files
245 
246             scanner = buildContext.newDeleteScanner( resourceDirectory );
247 
248             setupScanner( resource, scanner, mavenResourcesExecution.isAddDefaultExcludes() );
249 
250             scanner.scan();
251 
252             List<String> deletedFiles = Arrays.asList( scanner.getIncludedFiles() );
253 
254             for ( String name : deletedFiles )
255             {
256                 File destinationFile = getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );
257 
258                 destinationFile.delete();
259 
260                 buildContext.refresh( destinationFile );
261             }
262 
263         }
264 
265     }
266 
267     private File getDestinationFile( File outputDirectory, String targetPath, String name,
268                                      MavenResourcesExecution mavenResourcesExecution )
269                                          throws MavenFilteringException
270     {
271         String destination = name;
272 
273         if ( mavenResourcesExecution.isFilterFilenames() && mavenResourcesExecution.getFilterWrappers().size() > 0 )
274         {
275             destination = filterFileName( destination, mavenResourcesExecution.getFilterWrappers() );
276         }
277 
278         if ( targetPath != null )
279         {
280             destination = targetPath + "/" + destination;
281         }
282 
283         File destinationFile = new File( destination );
284         if ( !destinationFile.isAbsolute() )
285         {
286             destinationFile = new File( outputDirectory, destination );
287         }
288 
289         if ( !destinationFile.getParentFile().exists() )
290         {
291             destinationFile.getParentFile().mkdirs();
292         }
293         return destinationFile;
294     }
295 
296     private String[] setupScanner( Resource resource, Scanner scanner, boolean addDefaultExcludes )
297     {
298         String[] includes = null;
299         if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
300         {
301             includes = (String[]) resource.getIncludes().toArray( EMPTY_STRING_ARRAY );
302         }
303         else
304         {
305             includes = DEFAULT_INCLUDES;
306         }
307         scanner.setIncludes( includes );
308 
309         String[] excludes = null;
310         if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
311         {
312             excludes = (String[]) resource.getExcludes().toArray( EMPTY_STRING_ARRAY );
313             scanner.setExcludes( excludes );
314         }
315 
316         if ( addDefaultExcludes )
317         {
318             scanner.addDefaultExcludes();
319         }
320         return includes;
321     }
322 
323     private void copyDirectoryLayout( File sourceDirectory, File destinationDirectory, Scanner scanner )
324         throws IOException
325     {
326         if ( sourceDirectory == null )
327         {
328             throw new IOException( "source directory can't be null." );
329         }
330 
331         if ( destinationDirectory == null )
332         {
333             throw new IOException( "destination directory can't be null." );
334         }
335 
336         if ( sourceDirectory.equals( destinationDirectory ) )
337         {
338             throw new IOException( "source and destination are the same directory." );
339         }
340 
341         if ( !sourceDirectory.exists() )
342         {
343             throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
344         }
345 
346         List<String> includedDirectories = Arrays.asList( scanner.getIncludedDirectories() );
347 
348         for ( String name : includedDirectories )
349         {
350             File source = new File( sourceDirectory, name );
351 
352             if ( source.equals( sourceDirectory ) )
353             {
354                 continue;
355             }
356 
357             File destination = new File( destinationDirectory, name );
358             destination.mkdirs();
359         }
360     }
361 
362     private String getRelativeOutputDirectory( MavenResourcesExecution execution )
363     {
364         String relOutDir = execution.getOutputDirectory().getAbsolutePath();
365 
366         if ( execution.getMavenProject() != null && execution.getMavenProject().getBasedir() != null )
367         {
368             String basedir = execution.getMavenProject().getBasedir().getAbsolutePath();
369             relOutDir = PathTool.getRelativeFilePath( basedir, relOutDir );
370             if ( relOutDir == null )
371             {
372                 relOutDir = execution.getOutputDirectory().getPath();
373             }
374             else
375             {
376                 relOutDir = relOutDir.replace( '\\', '/' );
377             }
378         }
379 
380         return relOutDir;
381     }
382 
383     /*
384      * Filter the name of a file using the same mechanism for filtering the content of the file.
385      */
386     private String filterFileName( String name, List<FilterWrapper> wrappers )
387         throws MavenFilteringException
388     {
389 
390         Reader reader = new StringReader( name );
391         for ( FilterWrapper wrapper : wrappers )
392         {
393             reader = wrapper.getReader( reader );
394         }
395 
396         StringWriter writer = new StringWriter();
397 
398         try
399         {
400             IOUtil.copy( reader, writer );
401         }
402         catch ( IOException e )
403         {
404             throw new MavenFilteringException( "Failed filtering filename" + name, e );
405         }
406 
407         String filteredFilename = writer.toString();
408 
409         if ( getLogger().isDebugEnabled() )
410         {
411             getLogger().debug( "renaming filename " + name + " to " + filteredFilename );
412         }
413         return filteredFilename;
414     }
415 
416 }