View Javadoc
1   package org.apache.maven.plugins.resources;
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.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.LinkedHashSet;
27  import java.util.List;
28  import java.util.Properties;
29  
30  import org.apache.maven.execution.MavenSession;
31  import org.apache.maven.model.Resource;
32  import org.apache.maven.plugin.AbstractMojo;
33  import org.apache.maven.plugin.MojoExecutionException;
34  import org.apache.maven.plugins.annotations.Component;
35  import org.apache.maven.plugins.annotations.LifecyclePhase;
36  import org.apache.maven.plugins.annotations.Mojo;
37  import org.apache.maven.plugins.annotations.Parameter;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.shared.filtering.MavenFilteringException;
40  import org.apache.maven.shared.filtering.MavenResourcesExecution;
41  import org.apache.maven.shared.filtering.MavenResourcesFiltering;
42  import org.codehaus.plexus.PlexusConstants;
43  import org.codehaus.plexus.PlexusContainer;
44  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
45  import org.codehaus.plexus.context.Context;
46  import org.codehaus.plexus.context.ContextException;
47  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
48  import org.codehaus.plexus.util.ReaderFactory;
49  import org.codehaus.plexus.util.StringUtils;
50  
51  /**
52   * Copy resources for the main source code to the main output directory. Always uses the project.build.resources element
53   * to specify the resources to copy.
54   *
55   * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
56   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
57   * @author Andreas Hoheneder
58   * @author William Ferguson
59   */
60  @Mojo( name = "resources", defaultPhase = LifecyclePhase.PROCESS_RESOURCES, requiresProject = true, threadSafe = true )
61  public class ResourcesMojo
62      extends AbstractMojo
63      implements Contextualizable
64  {
65  
66      /**
67       * The character encoding scheme to be applied when filtering resources.
68       */
69      @Parameter( defaultValue = "${project.build.sourceEncoding}" )
70      protected String encoding;
71  
72      /**
73       * The output directory into which to copy the resources.
74       */
75      @Parameter( defaultValue = "${project.build.outputDirectory}", required = true )
76      private File outputDirectory;
77  
78      /**
79       * The list of resources we want to transfer.
80       */
81      @Parameter( defaultValue = "${project.resources}", required = true, readonly = true )
82      private List<Resource> resources;
83  
84      /**
85       *
86       */
87      @Parameter( defaultValue = "${project}", readonly = true, required = true )
88      protected MavenProject project;
89  
90      /**
91       * The list of additional filter properties files to be used along with System and project properties, which would
92       * be used for the filtering. <br/>
93       * See also: {@link ResourcesMojo#filters}.
94       *
95       * @since 2.4
96       */
97      @Parameter( defaultValue = "${project.build.filters}", readonly = true )
98      protected List<String> buildFilters;
99  
100     /**
101      * The list of extra filter properties files to be used along with System properties, project properties, and filter
102      * properties files specified in the POM build/filters section, which should be used for the filtering during the
103      * current mojo execution. <br/>
104      * Normally, these will be configured from a plugin's execution section, to provide a different set of filters for a
105      * particular execution. For instance, starting in Maven 2.2.0, you have the option of configuring executions with
106      * the id's <code>default-resources</code> and <code>default-testResources</code> to supply different configurations
107      * for the two different types of resources. By supplying <code>extraFilters</code> configurations, you can separate
108      * which filters are used for which type of resource.
109      */
110     @Parameter
111     protected List<String> filters;
112 
113     /**
114      * If false, don't use the filters specified in the build/filters section of the POM when processing resources in
115      * this mojo execution. <br/>
116      * See also: {@link ResourcesMojo#buildFilters} and {@link ResourcesMojo#filters}
117      *
118      * @since 2.4
119      */
120     @Parameter( defaultValue = "true" )
121     protected boolean useBuildFilters;
122 
123     /**
124      *
125      */
126     @Component( role = MavenResourcesFiltering.class, hint = "default" )
127     protected MavenResourcesFiltering mavenResourcesFiltering;
128 
129     /**
130      *
131      */
132     @Parameter( defaultValue = "${session}", readonly = true, required = true )
133     protected MavenSession session;
134 
135     /**
136      * Expressions preceded with this string won't be interpolated. Anything else preceded with this string will be
137      * passed through unchanged. For example {@code \${foo}} will be replaced with {@code ${foo}} but {@code \\${foo}}
138      * will be replaced with {@code \\value of foo}, if this parameter has been set to the backslash.
139      * <br/>
140      * @since 2.3
141      */
142     @Parameter
143     protected String escapeString;
144 
145     /**
146      * Overwrite existing files even if the destination files are newer.
147      *
148      * @since 2.3
149      */
150     @Parameter( defaultValue = "false" )
151     private boolean overwrite;
152 
153     /**
154      * Copy any empty directories included in the Resources.
155      *
156      * @since 2.3
157      */
158     @Parameter( defaultValue = "false" )
159     protected boolean includeEmptyDirs;
160 
161     /**
162      * Additional file extensions to not apply filtering (already defined are : jpg, jpeg, gif, bmp, png)
163      *
164      * @since 2.3
165      */
166     @Parameter
167     protected List<String> nonFilteredFileExtensions;
168 
169     /**
170      * Whether to escape backslashes and colons in windows-style paths.
171      *
172      * @since 2.4
173      */
174     @Parameter( defaultValue = "true" )
175     protected boolean escapeWindowsPaths;
176 
177     /**
178      * <p>
179      * Set of delimiters for expressions to filter within the resources. These delimiters are specified in the form
180      * {@code beginToken*endToken}. If no {@code *} is given, the delimiter is assumed to be the same for start and end.
181      * </p>
182      * <p>
183      * So, the default filtering delimiters might be specified as:
184      * </p>
185      * 
186      * <pre>
187      * &lt;delimiters&gt;
188      *   &lt;delimiter&gt;${*}&lt;/delimiter&gt;
189      *   &lt;delimiter&gt;@&lt;/delimiter&gt;
190      * &lt;/delimiters&gt;
191      * </pre>
192      * <p>
193      * Since the {@code @} delimiter is the same on both ends, we don't need to specify {@code @*@} (though we can).
194      * </p>
195      *
196      * @since 2.4
197      */
198     @Parameter
199     protected LinkedHashSet<String> delimiters;
200 
201     /**
202      * Use default delimiters in addition to custom delimiters, if any.
203      *
204      * @since 2.4
205      */
206     @Parameter( defaultValue = "true" )
207     protected boolean useDefaultDelimiters;
208 
209     /**
210      * By default files like {@code .gitignore}, {@code .cvsignore} etc. are excluded which means they will not being
211      * copied. If you need them for a particular reason you can do that by settings this to {@code false}. This means
212      * all files like the following will be copied.
213      * <ul>
214      * <li>Misc: &#42;&#42;/&#42;~, &#42;&#42;/#&#42;#, &#42;&#42;/.#&#42;, &#42;&#42;/%&#42;%, &#42;&#42;/._&#42;</li>
215      * <li>CVS: &#42;&#42;/CVS, &#42;&#42;/CVS/&#42;&#42;, &#42;&#42;/.cvsignore</li>
216      * <li>RCS: &#42;&#42;/RCS, &#42;&#42;/RCS/&#42;&#42;</li>
217      * <li>SCCS: &#42;&#42;/SCCS, &#42;&#42;/SCCS/&#42;&#42;</li>
218      * <li>VSSercer: &#42;&#42;/vssver.scc</li>
219      * <li>MKS: &#42;&#42;/project.pj</li>
220      * <li>SVN: &#42;&#42;/.svn, &#42;&#42;/.svn/&#42;&#42;</li>
221      * <li>GNU: &#42;&#42;/.arch-ids, &#42;&#42;/.arch-ids/&#42;&#42;</li>
222      * <li>Bazaar: &#42;&#42;/.bzr, &#42;&#42;/.bzr/&#42;&#42;</li>
223      * <li>SurroundSCM: &#42;&#42;/.MySCMServerInfo</li>
224      * <li>Mac: &#42;&#42;/.DS_Store</li>
225      * <li>Serena Dimension: &#42;&#42;/.metadata, &#42;&#42;/.metadata/&#42;&#42;</li>
226      * <li>Mercurial: &#42;&#42;/.hg, &#42;&#42;/.hg/&#42;&#42;</li>
227      * <li>GIT: &#42;&#42;/.git, &#42;&#42;/.gitignore, &#42;&#42;/.gitattributes, &#42;&#42;/.git/&#42;&#42;</li>
228      * <li>Bitkeeper: &#42;&#42;/BitKeeper, &#42;&#42;/BitKeeper/&#42;&#42;, &#42;&#42;/ChangeSet,
229      * &#42;&#42;/ChangeSet/&#42;&#42;</li>
230      * <li>Darcs: &#42;&#42;/_darcs, &#42;&#42;/_darcs/&#42;&#42;, &#42;&#42;/.darcsrepo,
231      * &#42;&#42;/.darcsrepo/&#42;&#42;&#42;&#42;/-darcs-backup&#42;, &#42;&#42;/.darcs-temp-mail
232      * </ul>
233      *
234      * @since 3.0.0
235      */
236     @Parameter( defaultValue = "true" )
237     protected boolean addDefaultExcludes;
238 
239     /**
240      * <p>
241      * List of plexus components hint which implements
242      * {@link MavenResourcesFiltering#filterResources(MavenResourcesExecution)}. They will be executed after the
243      * resources copying/filtering.
244      * </p>
245      *
246      * @since 2.4
247      */
248     @Parameter
249     private List<String> mavenFilteringHints;
250 
251     /**
252      * @since 2.4
253      */
254     private PlexusContainer plexusContainer;
255 
256     /**
257      * @since 2.4
258      */
259     private List<MavenResourcesFiltering> mavenFilteringComponents = new ArrayList<MavenResourcesFiltering>();
260 
261     /**
262      * stop searching endToken at the end of line
263      *
264      * @since 2.5
265      */
266     @Parameter( defaultValue = "false" )
267     private boolean supportMultiLineFiltering;
268 
269     /**
270      * Support filtering of filenames folders etc.
271      * 
272      * @since 3.0.0
273      */
274     @Parameter( defaultValue = "false" )
275     private boolean fileNameFiltering;
276 
277     /**
278      * You can skip the execution of the plugin if you need to. Its use is NOT RECOMMENDED, but quite convenient on
279      * occasion.
280      * 
281      * @since 3.0.0
282      */
283     @Parameter( property = "maven.resources.skip", defaultValue = "false" )
284     private boolean skip;
285 
286     /** {@inheritDoc} */
287     public void contextualize( Context context )
288         throws ContextException
289     {
290         plexusContainer = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
291     }
292 
293     /** {@inheritDoc} */
294     public void execute()
295         throws MojoExecutionException
296     {
297         if ( isSkip() )
298         {
299             getLog().info( "Skipping the execution." );
300             return;
301         }
302 
303         if ( StringUtils.isEmpty( encoding ) && isFilteringEnabled( getResources() ) )
304         {
305             getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
306                 + ", i.e. build is platform dependent!" );
307         }
308 
309         try
310         {
311 
312             List<String> combinedFilters = getCombinedFiltersList();
313 
314             MavenResourcesExecution mavenResourcesExecution =
315                 new MavenResourcesExecution( getResources(), getOutputDirectory(), project, encoding, combinedFilters,
316                                              Collections.<String>emptyList(), session );
317 
318             mavenResourcesExecution.setEscapeWindowsPaths( escapeWindowsPaths );
319 
320             // never include project build filters in this call, since we've already accounted for the POM build filters
321             // above, in getCombinedFiltersList().
322             mavenResourcesExecution.setInjectProjectBuildFilters( false );
323 
324             mavenResourcesExecution.setEscapeString( escapeString );
325             mavenResourcesExecution.setOverwrite( overwrite );
326             mavenResourcesExecution.setIncludeEmptyDirs( includeEmptyDirs );
327             mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering );
328             mavenResourcesExecution.setFilterFilenames( fileNameFiltering );
329             mavenResourcesExecution.setAddDefaultExcludes( addDefaultExcludes );
330 
331             // Handle subject of MRESOURCES-99
332             Properties additionalProperties = addSeveralSpecialProperties();
333             mavenResourcesExecution.setAdditionalProperties( additionalProperties );
334 
335             // if these are NOT set, just use the defaults, which are '${*}' and '@'.
336             mavenResourcesExecution.setDelimiters( delimiters, useDefaultDelimiters );
337 
338             if ( nonFilteredFileExtensions != null )
339             {
340                 mavenResourcesExecution.setNonFilteredFileExtensions( nonFilteredFileExtensions );
341             }
342             mavenResourcesFiltering.filterResources( mavenResourcesExecution );
343 
344             executeUserFilterComponents( mavenResourcesExecution );
345         }
346         catch ( MavenFilteringException e )
347         {
348             throw new MojoExecutionException( e.getMessage(), e );
349         }
350     }
351 
352     /**
353      * This solves https://issues.apache.org/jira/browse/MRESOURCES-99.<br/>
354      * BUT:<br/>
355      * This should be done different than defining those properties a second time, cause they have already being defined
356      * in Maven Model Builder (package org.apache.maven.model.interpolation) via BuildTimestampValueSource. But those
357      * can't be found in the context which can be got from the maven core.<br/>
358      * A solution could be to put those values into the context by Maven core so they are accessible everywhere. (I'm
359      * not sure if this is a good idea). Better ideas are always welcome.
360      * 
361      * The problem at the moment is that maven core handles usage of properties and replacements in 
362      * the model, but does not the resource filtering which needed some of the properties.
363      * 
364      * @return the new instance with those properties.
365      */
366     private Properties addSeveralSpecialProperties()
367     {
368         String timeStamp = new MavenBuildTimestamp().formattedTimestamp();
369         Properties additionalProperties = new Properties();
370         additionalProperties.put( "maven.build.timestamp", timeStamp );
371         if ( project.getBasedir() != null )
372         {
373             additionalProperties.put( "project.baseUri", project.getBasedir().getAbsoluteFile().toURI().toString() );
374         }
375 
376         return additionalProperties;
377     }
378 
379     /**
380      * @param mavenResourcesExecution {@link MavenResourcesExecution} 
381      * @throws MojoExecutionException in case of wrong lookup.
382      * @throws MavenFilteringException in case of failure.
383      * @since 2.5
384      */
385     protected void executeUserFilterComponents( MavenResourcesExecution mavenResourcesExecution )
386         throws MojoExecutionException, MavenFilteringException
387     {
388 
389         if ( mavenFilteringHints != null )
390         {
391             for ( String hint : mavenFilteringHints )
392             {
393                 try
394                 {
395                     // CHECKSTYLE_OFF: LineLength
396                     mavenFilteringComponents.add( (MavenResourcesFiltering) plexusContainer.lookup( MavenResourcesFiltering.class.getName(),
397                                                                                                     hint ) );
398                     // CHECKSTYLE_ON: LineLength
399                 }
400                 catch ( ComponentLookupException e )
401                 {
402                     throw new MojoExecutionException( e.getMessage(), e );
403                 }
404             }
405         }
406         else
407         {
408             getLog().debug( "no use filter components" );
409         }
410 
411         if ( mavenFilteringComponents != null && !mavenFilteringComponents.isEmpty() )
412         {
413             getLog().debug( "execute user filters" );
414             for ( MavenResourcesFiltering filter : mavenFilteringComponents )
415             {
416                 filter.filterResources( mavenResourcesExecution );
417             }
418         }
419     }
420 
421     /**
422      * @return The combined filters.
423      */
424     protected List<String> getCombinedFiltersList()
425     {
426         if ( filters == null || filters.isEmpty() )
427         {
428             return useBuildFilters ? buildFilters : null;
429         }
430         else
431         {
432             List<String> result = new ArrayList<String>();
433 
434             if ( useBuildFilters && buildFilters != null && !buildFilters.isEmpty() )
435             {
436                 result.addAll( buildFilters );
437             }
438 
439             result.addAll( filters );
440 
441             return result;
442         }
443     }
444 
445     /**
446      * Determines whether filtering has been enabled for any resource.
447      *
448      * @param theResources The set of resources to check for filtering, may be <code>null</code>.
449      * @return <code>true</code> if at least one resource uses filtering, <code>false</code> otherwise.
450      */
451     private boolean isFilteringEnabled( Collection<Resource> theResources )
452     {
453         if ( theResources != null )
454         {
455             for ( Resource resource : theResources )
456             {
457                 if ( resource.isFiltering() )
458                 {
459                     return true;
460                 }
461             }
462         }
463         return false;
464     }
465 
466     /**
467      * @return {@link #resources}
468      */
469     public List<Resource> getResources()
470     {
471         return resources;
472     }
473 
474     /**
475      * @param resources set {@link #resources}
476      */
477     public void setResources( List<Resource> resources )
478     {
479         this.resources = resources;
480     }
481 
482     /**
483      * @return {@link #outputDirectory}
484      */
485     public File getOutputDirectory()
486     {
487         return outputDirectory;
488     }
489 
490     /**
491      * @param outputDirectory the output folder.
492      */
493     public void setOutputDirectory( File outputDirectory )
494     {
495         this.outputDirectory = outputDirectory;
496     }
497 
498     /**
499      * @return {@link #overwrite}
500      */
501     public boolean isOverwrite()
502     {
503         return overwrite;
504     }
505 
506     /**
507      * @param overwrite true to overwrite false otherwise.
508      */
509     public void setOverwrite( boolean overwrite )
510     {
511         this.overwrite = overwrite;
512     }
513 
514     /**
515      * @return {@link #includeEmptyDirs}
516      */
517     public boolean isIncludeEmptyDirs()
518     {
519         return includeEmptyDirs;
520     }
521 
522     /**
523      * @param includeEmptyDirs true/false.
524      */
525     public void setIncludeEmptyDirs( boolean includeEmptyDirs )
526     {
527         this.includeEmptyDirs = includeEmptyDirs;
528     }
529 
530     /**
531      * @return {@link #filters}
532      */
533     public List<String> getFilters()
534     {
535         return filters;
536     }
537 
538     /**
539      * @param filters The filters to use.
540      */
541     public void setFilters( List<String> filters )
542     {
543         this.filters = filters;
544     }
545 
546     /**
547      * @return {@link #delimiters}
548      */
549     public LinkedHashSet<String> getDelimiters()
550     {
551         return delimiters;
552     }
553 
554     /**
555      * @param delimiters The delimiters to use.
556      */
557     public void setDelimiters( LinkedHashSet<String> delimiters )
558     {
559         this.delimiters = delimiters;
560     }
561 
562     /**
563      * @return {@link #useDefaultDelimiters}
564      */
565     public boolean isUseDefaultDelimiters()
566     {
567         return useDefaultDelimiters;
568     }
569 
570     /**
571      * @param useDefaultDelimiters true to use {@code ${*}}
572      */
573     public void setUseDefaultDelimiters( boolean useDefaultDelimiters )
574     {
575         this.useDefaultDelimiters = useDefaultDelimiters;
576     }
577 
578     /**
579      * @return {@link #skip}
580      */
581     public boolean isSkip()
582     {
583         return skip;
584     }
585 
586 }