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          this.defaultNonFilteredFileExtensions = new ArrayList<String>( 5 );
75          this.defaultNonFilteredFileExtensions.add( "jpg" );
76          this.defaultNonFilteredFileExtensions.add( "jpeg" );
77          this.defaultNonFilteredFileExtensions.add( "gif" );
78          this.defaultNonFilteredFileExtensions.add( "bmp" );
79          this.defaultNonFilteredFileExtensions.add( "png" );
80      }
81  
82      /** {@inheritDoc} */
83      public boolean filteredFileExtension( String fileName, List<String> userNonFilteredFileExtensions )
84      {
85          List<String> nonFilteredFileExtensions = new ArrayList<String>( getDefaultNonFilteredFileExtensions() );
86          if ( userNonFilteredFileExtensions != null )
87          {
88              nonFilteredFileExtensions.addAll( userNonFilteredFileExtensions );
89          }
90          boolean filteredFileExtension =
91              !nonFilteredFileExtensions.contains( StringUtils.lowerCase( FileUtils.extension( fileName ) ) );
92          if ( getLogger().isDebugEnabled() )
93          {
94              getLogger().debug( "file " + fileName + " has a" + ( filteredFileExtension ? " " : " non " )
95                  + "filtered file extension" );
96          }
97          return filteredFileExtension;
98      }
99  
100     /** {@inheritDoc} */
101     public List<String> getDefaultNonFilteredFileExtensions()
102     {
103         if ( this.defaultNonFilteredFileExtensions == null )
104         {
105             this.defaultNonFilteredFileExtensions = new ArrayList<String>();
106         }
107         return this.defaultNonFilteredFileExtensions;
108     }
109 
110     /** {@inheritDoc} */
111     public void filterResources( MavenResourcesExecution mavenResourcesExecution )
112         throws MavenFilteringException
113     {
114         if ( mavenResourcesExecution == null )
115         {
116             throw new MavenFilteringException( "mavenResourcesExecution cannot be null" );
117         }
118 
119         if ( mavenResourcesExecution.getResources() == null )
120         {
121             getLogger().info( "No resources configured skip copying/filtering" );
122             return;
123         }
124 
125         if ( mavenResourcesExecution.getOutputDirectory() == null )
126         {
127             throw new MavenFilteringException( "outputDirectory cannot be null" );
128         }
129 
130         if ( mavenResourcesExecution.isUseDefaultFilterWrappers() )
131         {
132             handleDefaultFilterWrappers( mavenResourcesExecution );
133         }
134 
135         if ( mavenResourcesExecution.getEncoding() == null || mavenResourcesExecution.getEncoding().length() < 1 )
136         {
137             getLogger().warn( "Using platform encoding (" + ReaderFactory.FILE_ENCODING
138                 + " actually) to copy filtered resources, i.e. build is platform dependent!" );
139         }
140         else
141         {
142             getLogger().info( "Using '" + mavenResourcesExecution.getEncoding()
143                 + "' encoding to copy filtered resources." );
144         }
145 
146         for ( Resource resource : mavenResourcesExecution.getResources() )
147         {
148 
149             if ( getLogger().isDebugEnabled() )
150             {
151                 String ls = System.getProperty( "line.separator" );
152                 StringBuilder debugMessage =
153                     new StringBuilder( "resource with targetPath " ).append( resource.getTargetPath() ).append( ls );
154                 debugMessage.append( "directory " ).append( resource.getDirectory() ).append( ls );
155 
156                 // @formatter:off
157                 debugMessage.append( "excludes " ).append( resource.getExcludes() == null ? " empty "
158                                 : resource.getExcludes().toString() ).append( ls );
159                 debugMessage.append( "includes " ).append( resource.getIncludes() == null ? " empty "
160                                 : resource.getIncludes().toString() );
161 
162                 // @formatter:on
163                 getLogger().debug( debugMessage.toString() );
164             }
165 
166             String targetPath = resource.getTargetPath();
167 
168             File resourceDirectory = new File( resource.getDirectory() );
169 
170             if ( !resourceDirectory.isAbsolute() )
171             {
172                 resourceDirectory =
173                     new File( mavenResourcesExecution.getResourcesBaseDirectory(), resourceDirectory.getPath() );
174             }
175 
176             if ( !resourceDirectory.exists() )
177             {
178                 getLogger().info( "skip non existing resourceDirectory " + resourceDirectory.getPath() );
179                 continue;
180             }
181 
182             // this part is required in case the user specified "../something"
183             // as destination
184             // see MNG-1345
185             File outputDirectory = mavenResourcesExecution.getOutputDirectory();
186             boolean outputExists = outputDirectory.exists();
187             if ( !outputExists && !outputDirectory.mkdirs() )
188             {
189                 throw new MavenFilteringException( "Cannot create resource output directory: " + outputDirectory );
190             }
191 
192             boolean ignoreDelta = !outputExists || buildContext.hasDelta( mavenResourcesExecution.getFileFilters() )
193                 || buildContext.hasDelta( getRelativeOutputDirectory( mavenResourcesExecution ) );
194             getLogger().debug( "ignoreDelta " + ignoreDelta );
195             Scanner scanner = buildContext.newScanner( resourceDirectory, ignoreDelta );
196 
197             setupScanner( resource, scanner, mavenResourcesExecution.isAddDefaultExcludes() );
198 
199             scanner.scan();
200 
201             if ( mavenResourcesExecution.isIncludeEmptyDirs() )
202             {
203                 try
204                 {
205                     File targetDirectory =
206                         targetPath == null ? outputDirectory : new File( outputDirectory, targetPath );
207                     copyDirectoryLayout( resourceDirectory, targetDirectory, scanner );
208                 }
209                 catch ( IOException e )
210                 {
211                     throw new MavenFilteringException( "Cannot copy directory structure from "
212                         + resourceDirectory.getPath() + " to " + outputDirectory.getPath() );
213                 }
214             }
215 
216             List<String> includedFiles = Arrays.asList( scanner.getIncludedFiles() );
217 
218             getLogger().info( "Copying " + includedFiles.size() + " resource" + ( includedFiles.size() > 1 ? "s" : "" )
219                 + ( targetPath == null ? "" : " to " + targetPath ) );
220 
221             for ( String name : includedFiles )
222             {
223 
224                 getLogger().debug( "Copying file " + name );
225                 File source = new File( resourceDirectory, name );
226 
227                 File destinationFile = getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );
228 
229                 boolean filteredExt =
230                     filteredFileExtension( source.getName(), mavenResourcesExecution.getNonFilteredFileExtensions() );
231 
232                 mavenFileFilter.copyFile( source, destinationFile, resource.isFiltering() && filteredExt,
233                                           mavenResourcesExecution.getFilterWrappers(),
234                                           mavenResourcesExecution.getEncoding(),
235                                           mavenResourcesExecution.isOverwrite() );
236             }
237 
238             // deal with deleted source files
239 
240             scanner = buildContext.newDeleteScanner( resourceDirectory );
241 
242             setupScanner( resource, scanner, mavenResourcesExecution.isAddDefaultExcludes() );
243 
244             scanner.scan();
245 
246             List<String> deletedFiles = Arrays.asList( scanner.getIncludedFiles() );
247 
248             for ( String name : deletedFiles )
249             {
250                 File destinationFile = getDestinationFile( outputDirectory, targetPath, name, mavenResourcesExecution );
251 
252                 destinationFile.delete();
253 
254                 buildContext.refresh( destinationFile );
255             }
256 
257         }
258 
259     }
260 
261     private void handleDefaultFilterWrappers( MavenResourcesExecution mavenResourcesExecution )
262         throws MavenFilteringException
263     {
264         List<FileUtils.FilterWrapper> filterWrappers = new ArrayList<FileUtils.FilterWrapper>();
265         if ( mavenResourcesExecution.getFilterWrappers() != null )
266         {
267             filterWrappers.addAll( mavenResourcesExecution.getFilterWrappers() );
268         }
269         filterWrappers.addAll( mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution ) );
270         mavenResourcesExecution.setFilterWrappers( filterWrappers );
271     }
272 
273     private File getDestinationFile( File outputDirectory, String targetPath, String name,
274                                      MavenResourcesExecution mavenResourcesExecution )
275                                          throws MavenFilteringException
276     {
277         String destination = name;
278 
279         if ( mavenResourcesExecution.isFilterFilenames() && mavenResourcesExecution.getFilterWrappers().size() > 0 )
280         {
281             destination = filterFileName( destination, mavenResourcesExecution.getFilterWrappers() );
282         }
283 
284         if ( targetPath != null )
285         {
286             destination = targetPath + "/" + destination;
287         }
288 
289         File destinationFile = new File( destination );
290         if ( !destinationFile.isAbsolute() )
291         {
292             destinationFile = new File( outputDirectory, destination );
293         }
294 
295         if ( !destinationFile.getParentFile().exists() )
296         {
297             destinationFile.getParentFile().mkdirs();
298         }
299         return destinationFile;
300     }
301 
302     private String[] setupScanner( Resource resource, Scanner scanner, boolean addDefaultExcludes )
303     {
304         String[] includes = null;
305         if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
306         {
307             includes = (String[]) resource.getIncludes().toArray( EMPTY_STRING_ARRAY );
308         }
309         else
310         {
311             includes = DEFAULT_INCLUDES;
312         }
313         scanner.setIncludes( includes );
314 
315         String[] excludes = null;
316         if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
317         {
318             excludes = (String[]) resource.getExcludes().toArray( EMPTY_STRING_ARRAY );
319             scanner.setExcludes( excludes );
320         }
321 
322         if ( addDefaultExcludes )
323         {
324             scanner.addDefaultExcludes();
325         }
326         return includes;
327     }
328 
329     private void copyDirectoryLayout( File sourceDirectory, File destinationDirectory, Scanner scanner )
330         throws IOException
331     {
332         if ( sourceDirectory == null )
333         {
334             throw new IOException( "source directory can't be null." );
335         }
336 
337         if ( destinationDirectory == null )
338         {
339             throw new IOException( "destination directory can't be null." );
340         }
341 
342         if ( sourceDirectory.equals( destinationDirectory ) )
343         {
344             throw new IOException( "source and destination are the same directory." );
345         }
346 
347         if ( !sourceDirectory.exists() )
348         {
349             throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
350         }
351 
352         List<String> includedDirectories = Arrays.asList( scanner.getIncludedDirectories() );
353 
354         for ( String name : includedDirectories )
355         {
356             File source = new File( sourceDirectory, name );
357 
358             if ( source.equals( sourceDirectory ) )
359             {
360                 continue;
361             }
362 
363             File destination = new File( destinationDirectory, name );
364             destination.mkdirs();
365         }
366     }
367 
368     private String getRelativeOutputDirectory( MavenResourcesExecution execution )
369     {
370         String relOutDir = execution.getOutputDirectory().getAbsolutePath();
371 
372         if ( execution.getMavenProject() != null && execution.getMavenProject().getBasedir() != null )
373         {
374             String basedir = execution.getMavenProject().getBasedir().getAbsolutePath();
375             relOutDir = PathTool.getRelativeFilePath( basedir, relOutDir );
376             if ( relOutDir == null )
377             {
378                 relOutDir = execution.getOutputDirectory().getPath();
379             }
380             else
381             {
382                 relOutDir = relOutDir.replace( '\\', '/' );
383             }
384         }
385 
386         return relOutDir;
387     }
388 
389     /*
390      * Filter the name of a file using the same mechanism for filtering the content of the file.
391      */
392     private String filterFileName( String name, List<FilterWrapper> wrappers )
393         throws MavenFilteringException
394     {
395 
396         Reader reader = new StringReader( name );
397         for ( FilterWrapper wrapper : wrappers )
398         {
399             reader = wrapper.getReader( reader );
400         }
401 
402         StringWriter writer = new StringWriter();
403 
404         try
405         {
406             IOUtil.copy( reader, writer );
407         }
408         catch ( IOException e )
409         {
410             throw new MavenFilteringException( "Failed filtering filename" + name, e );
411         }
412 
413         String filteredFilename = writer.toString();
414 
415         if ( getLogger().isDebugEnabled() )
416         {
417             getLogger().debug( "renaming filename " + name + " to " + filteredFilename );
418         }
419         return filteredFilename;
420     }
421 
422 }