package org.apache.maven.plugins.resources; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Properties; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; 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.PlexusConstants; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.context.Context; import org.codehaus.plexus.context.ContextException; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.StringUtils; /** * Copy resources for the main source code to the main output directory. Always uses the project.build.resources element * to specify the resources to copy. * * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a> * @author <a href="mailto:jason@maven.org">Jason van Zyl</a> * @author Andreas Hoheneder * @author William Ferguson */ @Mojo( name = "resources", defaultPhase = LifecyclePhase.PROCESS_RESOURCES, requiresProject = true, threadSafe = true ) public class ResourcesMojo extends AbstractMojo implements Contextualizable { /** * The character encoding scheme to be applied when filtering resources. */ @Parameter( defaultValue = "${project.build.sourceEncoding}" ) protected String encoding; /** * The output directory into which to copy the resources. */ @Parameter( defaultValue = "${project.build.outputDirectory}", required = true ) private File outputDirectory; /** * The list of resources we want to transfer. */ @Parameter( defaultValue = "${project.resources}", required = true, readonly = true ) private List<Resource> resources; /** * */ @Parameter( defaultValue = "${project}", readonly = true, required = true ) protected MavenProject project; /** * The list of additional filter properties files to be used along with System and project properties, which would * be used for the filtering. <br/> * See also: {@link ResourcesMojo#filters}. * * @since 2.4 */ @Parameter( defaultValue = "${project.build.filters}", readonly = true ) protected List<String> buildFilters; /** * The list of extra filter properties files to be used along with System properties, project properties, and filter * properties files specified in the POM build/filters section, which should be used for the filtering during the * current mojo execution. <br/> * Normally, these will be configured from a plugin's execution section, to provide a different set of filters for a * particular execution. For instance, starting in Maven 2.2.0, you have the option of configuring executions with * the id's <code>default-resources</code> and <code>default-testResources</code> to supply different configurations * for the two different types of resources. By supplying <code>extraFilters</code> configurations, you can separate * which filters are used for which type of resource. */ @Parameter protected List<String> filters; /** * If false, don't use the filters specified in the build/filters section of the POM when processing resources in * this mojo execution. <br/> * See also: {@link ResourcesMojo#buildFilters} and {@link ResourcesMojo#filters} * * @since 2.4 */ @Parameter( defaultValue = "true" ) protected boolean useBuildFilters; /** * */ @Component( role = MavenResourcesFiltering.class, hint = "default" ) protected MavenResourcesFiltering mavenResourcesFiltering; /** * */ @Parameter( defaultValue = "${session}", readonly = true, required = true ) protected MavenSession session; /** * Expressions preceded with this string won't be interpolated. Anything else preceded with this string will be * passed through unchanged. For example {@code \${foo}} will be replaced with {@code ${foo}} but {@code \\${foo}} * will be replaced with {@code \\value of foo}, if this parameter has been set to the backslash. * <br/> * @since 2.3 */ @Parameter protected String escapeString; /** * Overwrite existing files even if the destination files are newer. * * @since 2.3 */ @Parameter( defaultValue = "false" ) private boolean overwrite; /** * Copy any empty directories included in the Resources. * * @since 2.3 */ @Parameter( defaultValue = "false" ) protected boolean includeEmptyDirs; /** * Additional file extensions to not apply filtering (already defined are : jpg, jpeg, gif, bmp, png) * * @since 2.3 */ @Parameter protected List<String> nonFilteredFileExtensions; /** * Whether to escape backslashes and colons in windows-style paths. * * @since 2.4 */ @Parameter( defaultValue = "true" ) protected boolean escapeWindowsPaths; /** * <p> * Set of delimiters for expressions to filter within the resources. These delimiters are specified in the form * {@code beginToken*endToken}. If no {@code *} is given, the delimiter is assumed to be the same for start and end. * </p> * <p> * So, the default filtering delimiters might be specified as: * </p> * * <pre> * <delimiters> * <delimiter>${*}</delimiter> * <delimiter>@</delimiter> * </delimiters> * </pre> * <p> * Since the {@code @} delimiter is the same on both ends, we don't need to specify {@code @*@} (though we can). * </p> * * @since 2.4 */ @Parameter protected LinkedHashSet<String> delimiters; /** * Use default delimiters in addition to custom delimiters, if any. * * @since 2.4 */ @Parameter( defaultValue = "true" ) protected boolean useDefaultDelimiters; /** * By default files like {@code .gitignore}, {@code .cvsignore} etc. are excluded which means they will not being * copied. If you need them for a particular reason you can do that by settings this to {@code false}. This means * all files like the following will be copied. * <ul> * <li>Misc: **/*~, **/#*#, **/.#*, **/%*%, **/._*</li> * <li>CVS: **/CVS, **/CVS/**, **/.cvsignore</li> * <li>RCS: **/RCS, **/RCS/**</li> * <li>SCCS: **/SCCS, **/SCCS/**</li> * <li>VSSercer: **/vssver.scc</li> * <li>MKS: **/project.pj</li> * <li>SVN: **/.svn, **/.svn/**</li> * <li>GNU: **/.arch-ids, **/.arch-ids/**</li> * <li>Bazaar: **/.bzr, **/.bzr/**</li> * <li>SurroundSCM: **/.MySCMServerInfo</li> * <li>Mac: **/.DS_Store</li> * <li>Serena Dimension: **/.metadata, **/.metadata/**</li> * <li>Mercurial: **/.hg, **/.hg/**</li> * <li>GIT: **/.git, **/.gitignore, **/.gitattributes, **/.git/**</li> * <li>Bitkeeper: **/BitKeeper, **/BitKeeper/**, **/ChangeSet, * **/ChangeSet/**</li> * <li>Darcs: **/_darcs, **/_darcs/**, **/.darcsrepo, * **/.darcsrepo/****/-darcs-backup*, **/.darcs-temp-mail * </ul> * * @since 3.0.0 */ @Parameter( defaultValue = "true" ) protected boolean addDefaultExcludes; /** * <p> * List of plexus components hint which implements * {@link MavenResourcesFiltering#filterResources(MavenResourcesExecution)}. They will be executed after the * resources copying/filtering. * </p> * * @since 2.4 */ @Parameter private List<String> mavenFilteringHints; /** * @since 2.4 */ private PlexusContainer plexusContainer; /** * @since 2.4 */ private List<MavenResourcesFiltering> mavenFilteringComponents = new ArrayList<MavenResourcesFiltering>(); /** * stop searching endToken at the end of line * * @since 2.5 */ @Parameter( defaultValue = "false" ) private boolean supportMultiLineFiltering; /** * Support filtering of filenames folders etc. * * @since 3.0.0 */ @Parameter( defaultValue = "false" ) private boolean fileNameFiltering; /** * You can skip the execution of the plugin if you need to. Its use is NOT RECOMMENDED, but quite convenient on * occasion. * * @since 3.0.0 */ @Parameter( property = "maven.resources.skip", defaultValue = "false" ) private boolean skip; /** {@inheritDoc} */ public void contextualize( Context context ) throws ContextException { plexusContainer = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY ); } /** {@inheritDoc} */ public void execute() throws MojoExecutionException { if ( isSkip() ) { getLog().info( "Skipping the execution." ); return; } if ( StringUtils.isEmpty( encoding ) && isFilteringEnabled( getResources() ) ) { getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" ); } try { List<String> combinedFilters = getCombinedFiltersList(); MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution( getResources(), getOutputDirectory(), project, encoding, combinedFilters, Collections.<String>emptyList(), session ); mavenResourcesExecution.setEscapeWindowsPaths( escapeWindowsPaths ); // never include project build filters in this call, since we've already accounted for the POM build filters // above, in getCombinedFiltersList(). mavenResourcesExecution.setInjectProjectBuildFilters( false ); mavenResourcesExecution.setEscapeString( escapeString ); mavenResourcesExecution.setOverwrite( overwrite ); mavenResourcesExecution.setIncludeEmptyDirs( includeEmptyDirs ); mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering ); mavenResourcesExecution.setFilterFilenames( fileNameFiltering ); mavenResourcesExecution.setAddDefaultExcludes( addDefaultExcludes ); // Handle subject of MRESOURCES-99 Properties additionalProperties = addSeveralSpecialProperties(); mavenResourcesExecution.setAdditionalProperties( additionalProperties ); // if these are NOT set, just use the defaults, which are '${*}' and '@'. mavenResourcesExecution.setDelimiters( delimiters, useDefaultDelimiters ); if ( nonFilteredFileExtensions != null ) { mavenResourcesExecution.setNonFilteredFileExtensions( nonFilteredFileExtensions ); } mavenResourcesFiltering.filterResources( mavenResourcesExecution ); executeUserFilterComponents( mavenResourcesExecution ); } catch ( MavenFilteringException e ) { throw new MojoExecutionException( e.getMessage(), e ); } } /** * This solves https://issues.apache.org/jira/browse/MRESOURCES-99.<br/> * BUT:<br/> * This should be done different than defining those properties a second time, cause they have already being defined * in Maven Model Builder (package org.apache.maven.model.interpolation) via BuildTimestampValueSource. But those * can't be found in the context which can be got from the maven core.<br/> * A solution could be to put those values into the context by Maven core so they are accessible everywhere. (I'm * not sure if this is a good idea). Better ideas are always welcome. * * The problem at the moment is that maven core handles usage of properties and replacements in * the model, but does not the resource filtering which needed some of the properties. * * @return the new instance with those properties. */ private Properties addSeveralSpecialProperties() { String timeStamp = new MavenBuildTimestamp().formattedTimestamp(); Properties additionalProperties = new Properties(); additionalProperties.put( "maven.build.timestamp", timeStamp ); if ( project.getBasedir() != null ) { additionalProperties.put( "project.baseUri", project.getBasedir().getAbsoluteFile().toURI().toString() ); } return additionalProperties; } /** * @param mavenResourcesExecution {@link MavenResourcesExecution} * @throws MojoExecutionException in case of wrong lookup. * @throws MavenFilteringException in case of failure. * @since 2.5 */ protected void executeUserFilterComponents( MavenResourcesExecution mavenResourcesExecution ) throws MojoExecutionException, MavenFilteringException { if ( mavenFilteringHints != null ) { for ( String hint : mavenFilteringHints ) { try { // CHECKSTYLE_OFF: LineLength mavenFilteringComponents.add( (MavenResourcesFiltering) plexusContainer.lookup( MavenResourcesFiltering.class.getName(), hint ) ); // CHECKSTYLE_ON: LineLength } catch ( ComponentLookupException e ) { throw new MojoExecutionException( e.getMessage(), e ); } } } else { getLog().debug( "no use filter components" ); } if ( mavenFilteringComponents != null && !mavenFilteringComponents.isEmpty() ) { getLog().debug( "execute user filters" ); for ( MavenResourcesFiltering filter : mavenFilteringComponents ) { filter.filterResources( mavenResourcesExecution ); } } } /** * @return The combined filters. */ protected List<String> getCombinedFiltersList() { if ( filters == null || filters.isEmpty() ) { return useBuildFilters ? buildFilters : null; } else { List<String> result = new ArrayList<String>(); if ( useBuildFilters && buildFilters != null && !buildFilters.isEmpty() ) { result.addAll( buildFilters ); } result.addAll( filters ); return result; } } /** * Determines whether filtering has been enabled for any resource. * * @param theResources The set of resources to check for filtering, may be <code>null</code>. * @return <code>true</code> if at least one resource uses filtering, <code>false</code> otherwise. */ private boolean isFilteringEnabled( Collection<Resource> theResources ) { if ( theResources != null ) { for ( Resource resource : theResources ) { if ( resource.isFiltering() ) { return true; } } } return false; } /** * @return {@link #resources} */ public List<Resource> getResources() { return resources; } /** * @param resources set {@link #resources} */ public void setResources( List<Resource> resources ) { this.resources = resources; } /** * @return {@link #outputDirectory} */ public File getOutputDirectory() { return outputDirectory; } /** * @param outputDirectory the output folder. */ public void setOutputDirectory( File outputDirectory ) { this.outputDirectory = outputDirectory; } /** * @return {@link #overwrite} */ public boolean isOverwrite() { return overwrite; } /** * @param overwrite true to overwrite false otherwise. */ public void setOverwrite( boolean overwrite ) { this.overwrite = overwrite; } /** * @return {@link #includeEmptyDirs} */ public boolean isIncludeEmptyDirs() { return includeEmptyDirs; } /** * @param includeEmptyDirs true/false. */ public void setIncludeEmptyDirs( boolean includeEmptyDirs ) { this.includeEmptyDirs = includeEmptyDirs; } /** * @return {@link #filters} */ public List<String> getFilters() { return filters; } /** * @param filters The filters to use. */ public void setFilters( List<String> filters ) { this.filters = filters; } /** * @return {@link #delimiters} */ public LinkedHashSet<String> getDelimiters() { return delimiters; } /** * @param delimiters The delimiters to use. */ public void setDelimiters( LinkedHashSet<String> delimiters ) { this.delimiters = delimiters; } /** * @return {@link #useDefaultDelimiters} */ public boolean isUseDefaultDelimiters() { return useDefaultDelimiters; } /** * @param useDefaultDelimiters true to use {@code ${*}} */ public void setUseDefaultDelimiters( boolean useDefaultDelimiters ) { this.useDefaultDelimiters = useDefaultDelimiters; } /** * @return {@link #skip} */ public boolean isSkip() { return skip; } }