cstamas commented on code in PR #26: URL: https://github.com/apache/maven-remote-resources-plugin/pull/26#discussion_r1149036584
########## src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java: ########## @@ -0,0 +1,1358 @@ +package org.apache.maven.plugin.resources.remote; + +/* + * 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.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.io.output.DeferredFileOutputStream; +import org.apache.maven.RepositoryUtils; +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Model; +import org.apache.maven.model.Organization; +import org.apache.maven.model.Resource; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.resources.remote.io.xpp3.RemoteResourcesBundleXpp3Reader; +import org.apache.maven.plugin.resources.remote.io.xpp3.SupplementalDataModelXpp3Reader; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingResult; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; +import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter; +import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; +import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter; +import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter; +import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.apache.maven.shared.filtering.MavenFileFilterRequest; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.codehaus.plexus.resource.ResourceManager; +import org.codehaus.plexus.resource.loader.FileResourceLoader; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.util.artifact.JavaScopes; + +/** + * <p> + * Pull down resourceBundles containing remote resources and process the resources contained inside. When that is done, + * the resources are injected into the current (in-memory) Maven project, making them available to the process-resources + * phase. + * </p> + * <p> + * Resources that end in ".vm" are treated as Velocity templates. For those, the ".vm" is stripped off for the final + * artifact name and it's fed through Velocity to have properties expanded, conditions processed, etc... + * </p> + * Resources that don't end in ".vm" are copied "as is". + * <p> + * This is a support abstract class, with two non-aggregating and aggregating implementations. + * </p> + */ +public abstract class AbstractProcessRemoteResourcesMojo + extends AbstractMojo +{ + private static final String TEMPLATE_SUFFIX = ".vm"; + + /** + * <p> + * In cases where a local resource overrides one from a remote resource bundle, that resource should be filtered if + * the resource set specifies it. In those cases, this parameter defines the list of delimiters for filterable + * expressions. These delimiters are specified in the form 'beginToken*endToken'. If no '*' 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> + * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can). + * + * @since 1.1 + */ + @Parameter + protected List<String> filterDelimiters; + + /** + * @since 1.1 + */ + @Parameter( defaultValue = "true" ) + protected boolean useDefaultFilterDelimiters; + + /** + * The character encoding scheme to be applied when filtering resources. + */ + @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) + protected String encoding; + + /** + * The directory where processed resources will be placed for packaging. + */ + @Parameter( defaultValue = "${project.build.directory}/maven-shared-archive-resources" ) + private File outputDirectory; + + /** + * The directory containing extra information appended to the generated resources. + */ + @Parameter( defaultValue = "${basedir}/src/main/appended-resources" ) + private File appendedResourcesDirectory; + + /** + * Supplemental model data. Useful when processing + * artifacts with incomplete POM metadata. + * <p/> + * By default, this Mojo looks for supplemental model data in the file + * "<code>${appendedResourcesDirectory}/supplemental-models.xml</code>". + * + * @since 1.0-alpha-5 + */ + @Parameter + private String[] supplementalModels; + + /** + * List of artifacts that are added to the search path when looking + * for supplementalModels, expressed with <code>groupId:artifactId:version[:type[:classifier]]</code> format. + * + * @since 1.1 + */ + @Parameter + private List<String> supplementalModelArtifacts; + + /** + * The resource bundles that will be retrieved and processed, + * expressed with <code>groupId:artifactId:version[:type[:classifier]]</code> format. + */ + @Parameter( required = true ) + private List<String> resourceBundles; + + /** + * Skip remote-resource processing + * + * @since 1.0-alpha-5 + */ + @Parameter( property = "remoteresources.skip", defaultValue = "false" ) + private boolean skip; + + /** + * Attaches the resources to the main build of the project as a resource directory. + * + * @since 1.5 + */ + @Parameter( defaultValue = "true", property = "attachToMain" ) + private boolean attachToMain; + + /** + * Attaches the resources to the test build of the project as a resource directory. + * + * @since 1.5 + */ + @Parameter( defaultValue = "true", property = "attachToTest" ) + private boolean attachToTest; + + /** + * Additional properties to be passed to Velocity. + * Several properties are automatically added:<ul> + * <li><code>project</code> - the current MavenProject </li> + * <li><code>projects</code> - the list of dependency projects</li> + * <li><code>projectsSortedByOrganization</code> - the list of dependency projects sorted by organization</li> + * <li><code>projectTimespan</code> - the timespan of the current project (requires inceptionYear in pom)</li> + * <li><code>locator</code> - the ResourceManager that can be used to retrieve additional resources</li> + * </ul> + * See <a + * href="https://maven.apache.org/ref/current/maven-project/apidocs/org/apache/maven/project/MavenProject.html"> the + * javadoc for MavenProject</a> for information about the properties on the MavenProject. + */ + @Parameter + protected Map<String, Object> properties = new HashMap<>(); + + /** + * Whether to include properties defined in the project when filtering resources. + * + * @since 1.2 + */ + @Parameter( defaultValue = "false" ) + protected boolean includeProjectProperties = false; + + /** + * When the result of velocity transformation fits in memory, it is compared with the actual contents on disk + * to eliminate unnecessary destination file overwrite. This improves build times since further build steps + * typically rely on the modification date. + * + * @since 1.6 + */ + @Parameter( defaultValue = "5242880" ) + protected int velocityFilterInMemoryThreshold = 5 * 1024 * 1024; + + /** + * The Maven session. + */ + @Parameter( defaultValue = "${session}", readonly = true, required = true ) + protected MavenSession mavenSession; + + /** + * The current project. + */ + @Parameter( defaultValue = "${project}", readonly = true, required = true ) + protected MavenProject project; + + /** + * Scope to include. An Empty string indicates all scopes (default is "runtime"). + * + * @since 1.0 + */ + @Parameter( property = "includeScope", defaultValue = "runtime" ) + protected String includeScope; + + /** + * Scope to exclude. An Empty string indicates no scopes (default). + * + * @since 1.0 + */ + @Parameter( property = "excludeScope", defaultValue = "" ) + protected String excludeScope; + + /** + * When resolving project dependencies, specify the scopes to include. + * The default is the same as "includeScope" if there are no exclude scopes set. + * Otherwise, it defaults to "test" to grab all the dependencies so the + * exclude filters can filter out what is not needed. + * + * @since 1.5 + */ + @Parameter + protected String[] resolveScopes; + + /** + * Comma separated list of Artifact names to exclude. + * + * @since 1.0 + */ + @Parameter( property = "excludeArtifactIds", defaultValue = "" ) + protected String excludeArtifactIds; + + /** + * Comma separated list of Artifact names to include. + * + * @since 1.0 + */ + @Parameter( property = "includeArtifactIds", defaultValue = "" ) + protected String includeArtifactIds; + + /** + * Comma separated list of GroupId Names to exclude. + * + * @since 1.0 + */ + @Parameter( property = "excludeGroupIds", defaultValue = "" ) + protected String excludeGroupIds; + + /** + * Comma separated list of GroupIds to include. + * + * @since 1.0 + */ + @Parameter( property = "includeGroupIds", defaultValue = "" ) + protected String includeGroupIds; + + /** + * If we should exclude transitive dependencies + * + * @since 1.0 + */ + @Parameter( property = "excludeTransitive", defaultValue = "false" ) + protected boolean excludeTransitive; + + /** + * Timestamp for reproducible output archive entries, either formatted as ISO 8601 + * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like + * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>). + */ + @Parameter( defaultValue = "${project.build.outputTimestamp}" ) + private String outputTimestamp; + + @Component + protected RepositorySystem repositorySystem; + + /** + * Filtering support, for local resources that override those in the remote bundle. + */ + @Component + private MavenFileFilter fileFilter; + + @Component + private ResourceManager locator; + + @Component + private ProjectBuilder projectBuilder; + + @Component + private ArtifactHandlerManager artifactHandlerManager; + + /** + * Map of artifacts to supplemental project object models. + */ + private Map<String, Model> supplementModels; + + /** + * Merges supplemental data model with artifact metadata. Useful when processing artifacts with + * incomplete POM metadata. + */ + private final ModelInheritanceAssembler inheritanceAssembler = new ModelInheritanceAssembler(); + + private VelocityEngine velocity; + + @Override + public void execute() + throws MojoExecutionException + { + if ( skip ) + { + getLog().info( "Skipping remote resources execution." ); + return; + } + + if ( StringUtils.isEmpty( encoding ) ) + { + getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + + ", i.e. build is platform dependent!" ); + } + + if ( resolveScopes == null ) + { + resolveScopes = + new String[] {StringUtils.isEmpty( this.includeScope ) ? JavaScopes.TEST : this.includeScope}; + } + + if ( supplementalModels == null ) + { + File sups = new File( appendedResourcesDirectory, "supplemental-models.xml" ); + if ( sups.exists() ) + { + try + { + supplementalModels = new String[] {sups.toURI().toURL().toString()}; + } + catch ( MalformedURLException e ) + { + // ignore + getLog().debug( "URL issue with supplemental-models.xml: " + e ); + } + } + } + + configureLocator(); + + if ( includeProjectProperties ) + { + final Properties projectProperties = project.getProperties(); + for ( Object key : projectProperties.keySet() ) + { + properties.put( key.toString(), projectProperties.get( key ).toString() ); + } + } + + ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); + try + { + validate(); + + List<File> resourceBundleArtifacts = downloadBundles( resourceBundles ); + supplementModels = loadSupplements( supplementalModels ); + + ClassLoader classLoader = initalizeClassloader( resourceBundleArtifacts ); + + Thread.currentThread().setContextClassLoader( classLoader ); + + velocity = new VelocityEngine(); + velocity.setProperty( "resource.loaders", "classpath" ); + velocity.setProperty( "resource.loader.classpath.class", ClasspathResourceLoader.class.getName() ); + velocity.init(); + + VelocityContext context = buildVelocityContext( properties ); + + processResourceBundles( classLoader, context ); + + if ( outputDirectory.exists() ) + { + // ---------------------------------------------------------------------------- + // Push our newly generated resources directory into the MavenProject so that + // these resources can be picked up by the process-resources phase. + // ---------------------------------------------------------------------------- + Resource resource = new Resource(); + resource.setDirectory( outputDirectory.getAbsolutePath() ); + // MRRESOURCES-61 handle main and test resources separately + if ( attachToMain ) + { + project.getResources().add( resource ); + } + if ( attachToTest ) + { + project.getTestResources().add( resource ); + } + + // ---------------------------------------------------------------------------- + // Write out archiver dot file + // ---------------------------------------------------------------------------- + try + { + File dotFile = new File( project.getBuild().getDirectory(), ".plxarc" ); + FileUtils.mkdir( dotFile.getParentFile().getAbsolutePath() ); + FileUtils.fileWrite( dotFile.getAbsolutePath(), outputDirectory.getName() ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error creating dot file for archiving instructions.", e ); + } + } + } + finally + { + Thread.currentThread().setContextClassLoader( origLoader ); + } + } + + private void configureLocator() + throws MojoExecutionException + { + if ( supplementalModelArtifacts != null && !supplementalModelArtifacts.isEmpty() ) + { + List<File> artifacts = downloadBundles( supplementalModelArtifacts ); + + for ( File artifact : artifacts ) + { + if ( artifact.isDirectory() ) + { + locator.addSearchPath( FileResourceLoader.ID, artifact.getAbsolutePath() ); + } + else + { + try + { + locator.addSearchPath( "jar", "jar:" + artifact.toURI().toURL().toExternalForm() ); + } + catch ( MalformedURLException e ) + { + throw new MojoExecutionException( "Could not use jar " + artifact.getAbsolutePath(), e ); + } + } + } + + } + + locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() ); + if ( appendedResourcesDirectory != null ) + { + locator.addSearchPath( FileResourceLoader.ID, appendedResourcesDirectory.getAbsolutePath() ); + } + locator.addSearchPath( "url", "" ); + locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) ); + } + + protected List<MavenProject> getProjects() + { + List<MavenProject> projects = new ArrayList<>(); + + // add filters in well known order, least specific to most specific + FilterArtifacts filter = new FilterArtifacts(); + + Set<Artifact> artifacts = new LinkedHashSet<>(); + artifacts.addAll( getAllDependencies() ); + if ( this.excludeTransitive ) + { + filter.addFilter( new ProjectTransitivityFilter( getDirectDependencies(), true ) ); + } + + filter.addFilter( new ScopeFilter( this.includeScope, this.excludeScope ) ); + filter.addFilter( new GroupIdFilter( this.includeGroupIds, this.excludeGroupIds ) ); + filter.addFilter( new ArtifactIdFilter( this.includeArtifactIds, this.excludeArtifactIds ) ); + + // perform filtering + try + { + artifacts = filter.filter( artifacts ); + } + catch ( ArtifactFilterException e ) + { + throw new IllegalStateException( e.getMessage(), e ); + } + + getLog().debug( "PROJECTS: " + artifacts ); + + for ( Artifact artifact : artifacts ) + { + if ( artifact.isSnapshot() ) + { + artifact.setVersion( artifact.getBaseVersion() ); + } + + getLog().debug( "Building project for " + artifact ); + MavenProject p; + try + { + ProjectBuildingRequest req = new DefaultProjectBuildingRequest() + .setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ) + .setProcessPlugins( false ) + .setRepositorySession( mavenSession.getRepositorySession() ) + .setSystemProperties( mavenSession.getSystemProperties() ) + .setUserProperties( mavenSession.getUserProperties() ) + .setLocalRepository( mavenSession.getLocalRepository() ) + .setRemoteRepositories( project.getRemoteArtifactRepositories() ); + ProjectBuildingResult res = projectBuilder.build( artifact, req ); + p = res.getProject(); + } + catch ( ProjectBuildingException e ) + { + getLog().warn( "Invalid project model for artifact [" + artifact.getGroupId() + ":" + + artifact.getArtifactId() + ":" + artifact.getVersion() + "]. " + + "It will be ignored by the remote resources Mojo." ); + continue; + } + + String supplementKey = + generateSupplementMapKey( p.getModel().getGroupId(), p.getModel().getArtifactId() ); + + if ( supplementModels.containsKey( supplementKey ) ) + { + Model mergedModel = mergeModels( p.getModel(), supplementModels.get( supplementKey ) ); + MavenProject mergedProject = new MavenProject( mergedModel ); + projects.add( mergedProject ); + mergedProject.setArtifact( artifact ); + mergedProject.setVersion( artifact.getVersion() ); + getLog().debug( "Adding project with groupId [" + mergedProject.getGroupId() + "] (supplemented)" ); + } + else + { + projects.add( p ); + getLog().debug( "Adding project with groupId [" + p.getGroupId() + "]" ); + } + } + projects.sort( new ProjectComparator() ); + return projects; + } + + /** + * Returns all the transitive hull of all the involved maven projects. + */ + protected abstract Set<Artifact> getAllDependencies(); + + /** + * Returns all the direct dependencies of all the involved maven projects. + */ + protected abstract Set<Artifact> getDirectDependencies(); + + protected Map<Organization, List<MavenProject>> getProjectsSortedByOrganization( List<MavenProject> projects ) + { + Map<Organization, List<MavenProject>> organizations = + new TreeMap<>( new OrganizationComparator() ); + List<MavenProject> unknownOrganization = new ArrayList<>(); + + for ( MavenProject p : projects ) + { + if ( p.getOrganization() != null && StringUtils.isNotEmpty( p.getOrganization().getName() ) ) + { + List<MavenProject> sortedProjects = organizations.get( p.getOrganization() ); + if ( sortedProjects == null ) + { + sortedProjects = new ArrayList<>(); + } + sortedProjects.add( p ); + + organizations.put( p.getOrganization(), sortedProjects ); + } + else + { + unknownOrganization.add( p ); + } + } + if ( !unknownOrganization.isEmpty() ) + { + Organization unknownOrg = new Organization(); + unknownOrg.setName( "an unknown organization" ); + organizations.put( unknownOrg, unknownOrganization ); + } + + return organizations; + } + + protected boolean copyResourceIfExists( File file, String relFileName, VelocityContext context ) + throws IOException, MojoExecutionException + { + for ( Resource resource : project.getResources() ) + { + File resourceDirectory = new File( resource.getDirectory() ); + + if ( !resourceDirectory.exists() ) + { + continue; + } + + // TODO - really should use the resource includes/excludes and name mapping + File source = new File( resourceDirectory, relFileName ); + File templateSource = new File( resourceDirectory, relFileName + TEMPLATE_SUFFIX ); + + if ( !source.exists() && templateSource.exists() ) + { + source = templateSource; + } + + if ( source.exists() && !source.equals( file ) ) + { + if ( source == templateSource ) + { + try ( DeferredFileOutputStream os = + new DeferredFileOutputStream( velocityFilterInMemoryThreshold, file ) ) + { + try ( Reader reader = getReader( source ); Writer writer = getWriter( os ) ) + { + velocity.evaluate( context, writer, "", reader ); + } + catch ( ParseErrorException | MethodInvocationException | ResourceNotFoundException e ) + { + throw new MojoExecutionException( "Error rendering velocity resource: " + source, e ); + } + fileWriteIfDiffers( os ); + } + } + else if ( resource.isFiltering() ) + { + + MavenFileFilterRequest req = setupRequest( resource, source, file ); + + try + { + fileFilter.copyFile( req ); + } + catch ( MavenFilteringException e ) + { + throw new MojoExecutionException( "Error filtering resource: " + source, e ); + } + } + else + { + FileUtils.copyFile( source, file ); + } + + // exclude the original (so eclipse doesn't complain about duplicate resources) + resource.addExclude( relFileName ); + + return true; + } + + } + return false; + } + + private Reader getReader( File source ) throws IOException + { + if ( encoding != null ) + { + return new InputStreamReader( Files.newInputStream( source.toPath() ), encoding ); + } + else + { + return ReaderFactory.newPlatformReader( source ); + } + } + + private Writer getWriter( OutputStream os ) throws IOException + { + if ( encoding != null ) + { + return new OutputStreamWriter( os, encoding ); + } + else + { + return WriterFactory.newPlatformWriter( os ); + } + } + + /** + * If the transformation result fits in memory and the destination file already exists + * then both are compared. + * <p>If destination file is byte-by-byte equal, then it is not overwritten. + * This improves subsequent compilation times since upstream plugins property see that + * the resource was not modified. + * <p>Note: the method should be called after {@link DeferredFileOutputStream#close} + * + * @param outStream Deferred stream + * @throws IOException On IO error. + */ + private void fileWriteIfDiffers( DeferredFileOutputStream outStream ) + throws IOException + { + File file = outStream.getFile(); + if ( outStream.isThresholdExceeded() ) + { + getLog().info( + "File " + file + " was overwritten due to content limit threshold " + outStream.getThreshold() + + " reached" ); + return; + } + boolean needOverwrite = true; + + if ( file.exists() ) + { + try ( InputStream is = Files.newInputStream( file.toPath() ); + InputStream newContents = new ByteArrayInputStream( outStream.getData() ) ) + { + needOverwrite = !IOUtil.contentEquals( is, newContents ); + if ( getLog().isDebugEnabled() ) + { + getLog().debug( "File " + file + " contents " + ( needOverwrite ? "differs" : "does not differ" ) ); + } + } + } + + if ( !needOverwrite ) + { + getLog().debug( "File " + file + " is up to date" ); + return; + } + getLog().debug( "Writing " + file ); + + try ( OutputStream os = Files.newOutputStream( file.toPath() ) ) + { + outStream.writeTo( os ); + } + } + + private MavenFileFilterRequest setupRequest( Resource resource, File source, File file ) + { + MavenFileFilterRequest req = new MavenFileFilterRequest(); + req.setFrom( source ); + req.setTo( file ); + req.setFiltering( resource.isFiltering() ); + + req.setMavenProject( project ); + req.setMavenSession( mavenSession ); + req.setInjectProjectBuildFilters( true ); + + if ( encoding != null ) + { + req.setEncoding( encoding ); + } + + if ( filterDelimiters != null && !filterDelimiters.isEmpty() ) + { + LinkedHashSet<String> delims = new LinkedHashSet<>(); + if ( useDefaultFilterDelimiters ) + { + delims.addAll( req.getDelimiters() ); + } + + for ( String delim : filterDelimiters ) + { + if ( delim == null ) + { + delims.add( "${*}" ); + } + else + { + delims.add( delim ); + } + } + + req.setDelimiters( delims ); + } + + return req; + } + + protected void validate() + throws MojoExecutionException + { + int bundleCount = 1; + + for ( String artifactDescriptor : resourceBundles ) + { + // groupId:artifactId:version, groupId:artifactId:version:type + // or groupId:artifactId:version:type:classifier + String[] s = StringUtils.split( artifactDescriptor, ":" ); + + if ( s.length < 3 || s.length > 5 ) + { + String position; + + if ( bundleCount == 1 ) + { + position = "1st"; + } + else if ( bundleCount == 2 ) + { + position = "2nd"; + } + else if ( bundleCount == 3 ) + { + position = "3rd"; + } + else + { + position = bundleCount + "th"; + } + + throw new MojoExecutionException( "The " + position + + " resource bundle configured must specify a groupId, artifactId, " + + " version and, optionally, type and classifier for a remote resource bundle. " + + "Must be of the form <resourceBundle>groupId:artifactId:version</resourceBundle>, " + + "<resourceBundle>groupId:artifactId:version:type</resourceBundle> or " + + "<resourceBundle>groupId:artifactId:version:type:classifier</resourceBundle>" ); + } + + bundleCount++; + } + + } + + private static final String KEY_PROJECTS = "projects"; + private static final String KEY_PROJECTS_ORGS = "projectsSortedByOrganization"; + + protected VelocityContext buildVelocityContext( Map<String, Object> properties ) + { + // the following properties are expensive to calculate, so we provide them lazily + VelocityContext context = new VelocityContext( properties ) + { + @Override + public Object internalGet( String key ) + { + Object result = super.internalGet( key ); + if ( result == null && key != null && key.startsWith( KEY_PROJECTS ) && containsKey( key ) ) + { + // calculate and put projects* properties + List<MavenProject> projects = getProjects(); + put( KEY_PROJECTS, projects ); + put( KEY_PROJECTS_ORGS, getProjectsSortedByOrganization( projects ) ); + return super.internalGet( key ); + } + return result; + } + }; + // to have a consistent getKeys()/containsKey() behaviour, keys must be present from the start + context.put( KEY_PROJECTS, null ); + context.put( KEY_PROJECTS_ORGS, null ); + // the following properties are cheap to calculate, so we provide them eagerly + + // Reproducible Builds: try to use reproducible output timestamp + MavenArchiver archiver = new MavenArchiver(); + Date outputDate = archiver.parseOutputTimestamp( outputTimestamp ); + + String inceptionYear = project.getInceptionYear(); + String year = new SimpleDateFormat( "yyyy" ).format( ( outputDate == null ) ? new Date() : outputDate ); + + if ( StringUtils.isEmpty( inceptionYear ) ) + { + if ( getLog().isDebugEnabled() ) + { + getLog().debug( "inceptionYear not specified, defaulting to " + year ); + } + + inceptionYear = year; + } + context.put( "project", project ); + context.put( "presentYear", year ); + context.put( "locator", locator ); + + if ( inceptionYear.equals( year ) ) + { + context.put( "projectTimespan", year ); + } + else + { + context.put( "projectTimespan", inceptionYear + "-" + year ); + } + return context; + } + + private List<File> downloadBundles( List<String> bundles ) + throws MojoExecutionException + { + List<File> bundleArtifacts = new ArrayList<>(); + + for ( String artifactDescriptor : bundles ) + { + getLog().info( "Preparing remote bundle " + artifactDescriptor ); + // groupId:artifactId:version[:type[:classifier]] + String[] s = artifactDescriptor.split( ":" ); + + File artifactFile = null; + // check if the artifact is part of the reactor + if ( mavenSession != null ) + { + List<MavenProject> list = mavenSession.getProjects(); + for ( MavenProject p : list ) + { + if ( s[0].equals( p.getGroupId() ) && s[1].equals( p.getArtifactId() ) + && s[2].equals( p.getVersion() ) ) + { + if ( s.length >= 4 && "test-jar".equals( s[3] ) ) + { + artifactFile = new File( p.getBuild().getTestOutputDirectory() ); + } + else + { + artifactFile = new File( p.getBuild().getOutputDirectory() ); + } + } + } + } + if ( artifactFile == null || !artifactFile.exists() ) + { + String g = s[0]; + String a = s[1]; + String v = s[2]; + String type = ( s.length >= 4 ? s[3] : "jar" ); + ArtifactType artifactType = RepositoryUtils.newArtifactType( type, + artifactHandlerManager.getArtifactHandler( type ) ); + String classifier = ( s.length == 5 ? s[4] : artifactType.getClassifier() ); + + DefaultArtifact artifact = new DefaultArtifact( + g, a, classifier, artifactType.getExtension(), v, artifactType ); + + try + { + ArtifactRequest request = new ArtifactRequest( + artifact, + RepositoryUtils.toRepos( project.getRemoteArtifactRepositories() ), + "remote-resources" ); + ArtifactResult result = repositorySystem.resolveArtifact( + mavenSession.getRepositorySession(), request ); + artifactFile = result.getArtifact().getFile(); + } + catch ( ArtifactResolutionException e ) + { + throw new MojoExecutionException( "Error processing remote resources", e ); + } + + } + bundleArtifacts.add( artifactFile ); + } + + return bundleArtifacts; + } + + private ClassLoader initalizeClassloader( List<File> artifacts ) + throws MojoExecutionException + { + RemoteResourcesClassLoader cl = new RemoteResourcesClassLoader( null ); + try + { + for ( File artifact : artifacts ) + { + cl.addURL( artifact.toURI().toURL() ); + } + return cl; + } + catch ( MalformedURLException e ) + { + throw new MojoExecutionException( "Unable to configure resources classloader: " + e.getMessage(), e ); Review Comment: This is how it is in original code, did not touch this area. ########## src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java: ########## @@ -0,0 +1,1358 @@ +package org.apache.maven.plugin.resources.remote; + +/* + * 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.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.io.output.DeferredFileOutputStream; +import org.apache.maven.RepositoryUtils; +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Model; +import org.apache.maven.model.Organization; +import org.apache.maven.model.Resource; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.resources.remote.io.xpp3.RemoteResourcesBundleXpp3Reader; +import org.apache.maven.plugin.resources.remote.io.xpp3.SupplementalDataModelXpp3Reader; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingResult; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; +import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter; +import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; +import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter; +import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter; +import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.apache.maven.shared.filtering.MavenFileFilterRequest; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.codehaus.plexus.resource.ResourceManager; +import org.codehaus.plexus.resource.loader.FileResourceLoader; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.util.artifact.JavaScopes; + +/** + * <p> + * Pull down resourceBundles containing remote resources and process the resources contained inside. When that is done, + * the resources are injected into the current (in-memory) Maven project, making them available to the process-resources + * phase. + * </p> + * <p> + * Resources that end in ".vm" are treated as Velocity templates. For those, the ".vm" is stripped off for the final + * artifact name and it's fed through Velocity to have properties expanded, conditions processed, etc... + * </p> + * Resources that don't end in ".vm" are copied "as is". + * <p> + * This is a support abstract class, with two non-aggregating and aggregating implementations. + * </p> + */ +public abstract class AbstractProcessRemoteResourcesMojo + extends AbstractMojo +{ + private static final String TEMPLATE_SUFFIX = ".vm"; + + /** + * <p> + * In cases where a local resource overrides one from a remote resource bundle, that resource should be filtered if + * the resource set specifies it. In those cases, this parameter defines the list of delimiters for filterable + * expressions. These delimiters are specified in the form 'beginToken*endToken'. If no '*' 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> + * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can). + * + * @since 1.1 + */ + @Parameter + protected List<String> filterDelimiters; + + /** + * @since 1.1 + */ + @Parameter( defaultValue = "true" ) + protected boolean useDefaultFilterDelimiters; + + /** + * The character encoding scheme to be applied when filtering resources. + */ + @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) + protected String encoding; + + /** + * The directory where processed resources will be placed for packaging. + */ + @Parameter( defaultValue = "${project.build.directory}/maven-shared-archive-resources" ) + private File outputDirectory; + + /** + * The directory containing extra information appended to the generated resources. + */ + @Parameter( defaultValue = "${basedir}/src/main/appended-resources" ) + private File appendedResourcesDirectory; + + /** + * Supplemental model data. Useful when processing + * artifacts with incomplete POM metadata. + * <p/> + * By default, this Mojo looks for supplemental model data in the file + * "<code>${appendedResourcesDirectory}/supplemental-models.xml</code>". + * + * @since 1.0-alpha-5 + */ + @Parameter + private String[] supplementalModels; + + /** + * List of artifacts that are added to the search path when looking + * for supplementalModels, expressed with <code>groupId:artifactId:version[:type[:classifier]]</code> format. + * + * @since 1.1 + */ + @Parameter + private List<String> supplementalModelArtifacts; + + /** + * The resource bundles that will be retrieved and processed, + * expressed with <code>groupId:artifactId:version[:type[:classifier]]</code> format. + */ + @Parameter( required = true ) + private List<String> resourceBundles; + + /** + * Skip remote-resource processing + * + * @since 1.0-alpha-5 + */ + @Parameter( property = "remoteresources.skip", defaultValue = "false" ) + private boolean skip; + + /** + * Attaches the resources to the main build of the project as a resource directory. + * + * @since 1.5 + */ + @Parameter( defaultValue = "true", property = "attachToMain" ) + private boolean attachToMain; + + /** + * Attaches the resources to the test build of the project as a resource directory. + * + * @since 1.5 + */ + @Parameter( defaultValue = "true", property = "attachToTest" ) + private boolean attachToTest; + + /** + * Additional properties to be passed to Velocity. + * Several properties are automatically added:<ul> + * <li><code>project</code> - the current MavenProject </li> + * <li><code>projects</code> - the list of dependency projects</li> + * <li><code>projectsSortedByOrganization</code> - the list of dependency projects sorted by organization</li> + * <li><code>projectTimespan</code> - the timespan of the current project (requires inceptionYear in pom)</li> + * <li><code>locator</code> - the ResourceManager that can be used to retrieve additional resources</li> + * </ul> + * See <a + * href="https://maven.apache.org/ref/current/maven-project/apidocs/org/apache/maven/project/MavenProject.html"> the + * javadoc for MavenProject</a> for information about the properties on the MavenProject. + */ + @Parameter + protected Map<String, Object> properties = new HashMap<>(); + + /** + * Whether to include properties defined in the project when filtering resources. + * + * @since 1.2 + */ + @Parameter( defaultValue = "false" ) + protected boolean includeProjectProperties = false; Review Comment: This is how it is in original code, did not touch this area. ########## src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java: ########## @@ -0,0 +1,1358 @@ +package org.apache.maven.plugin.resources.remote; + +/* + * 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.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.io.output.DeferredFileOutputStream; +import org.apache.maven.RepositoryUtils; +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Model; +import org.apache.maven.model.Organization; +import org.apache.maven.model.Resource; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.resources.remote.io.xpp3.RemoteResourcesBundleXpp3Reader; +import org.apache.maven.plugin.resources.remote.io.xpp3.SupplementalDataModelXpp3Reader; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingResult; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; +import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter; +import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; +import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter; +import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter; +import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.apache.maven.shared.filtering.MavenFileFilterRequest; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.codehaus.plexus.resource.ResourceManager; +import org.codehaus.plexus.resource.loader.FileResourceLoader; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.util.artifact.JavaScopes; + +/** + * <p> + * Pull down resourceBundles containing remote resources and process the resources contained inside. When that is done, + * the resources are injected into the current (in-memory) Maven project, making them available to the process-resources + * phase. + * </p> + * <p> + * Resources that end in ".vm" are treated as Velocity templates. For those, the ".vm" is stripped off for the final + * artifact name and it's fed through Velocity to have properties expanded, conditions processed, etc... + * </p> + * Resources that don't end in ".vm" are copied "as is". + * <p> + * This is a support abstract class, with two non-aggregating and aggregating implementations. + * </p> + */ +public abstract class AbstractProcessRemoteResourcesMojo + extends AbstractMojo +{ + private static final String TEMPLATE_SUFFIX = ".vm"; + + /** + * <p> + * In cases where a local resource overrides one from a remote resource bundle, that resource should be filtered if + * the resource set specifies it. In those cases, this parameter defines the list of delimiters for filterable + * expressions. These delimiters are specified in the form 'beginToken*endToken'. If no '*' 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> + * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can). + * + * @since 1.1 + */ + @Parameter + protected List<String> filterDelimiters; + + /** + * @since 1.1 + */ + @Parameter( defaultValue = "true" ) + protected boolean useDefaultFilterDelimiters; + + /** + * The character encoding scheme to be applied when filtering resources. + */ + @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) + protected String encoding; + + /** + * The directory where processed resources will be placed for packaging. + */ + @Parameter( defaultValue = "${project.build.directory}/maven-shared-archive-resources" ) + private File outputDirectory; + + /** + * The directory containing extra information appended to the generated resources. + */ + @Parameter( defaultValue = "${basedir}/src/main/appended-resources" ) + private File appendedResourcesDirectory; + + /** + * Supplemental model data. Useful when processing + * artifacts with incomplete POM metadata. + * <p/> + * By default, this Mojo looks for supplemental model data in the file + * "<code>${appendedResourcesDirectory}/supplemental-models.xml</code>". + * + * @since 1.0-alpha-5 + */ + @Parameter + private String[] supplementalModels; + + /** + * List of artifacts that are added to the search path when looking + * for supplementalModels, expressed with <code>groupId:artifactId:version[:type[:classifier]]</code> format. + * + * @since 1.1 + */ + @Parameter + private List<String> supplementalModelArtifacts; + + /** + * The resource bundles that will be retrieved and processed, + * expressed with <code>groupId:artifactId:version[:type[:classifier]]</code> format. + */ + @Parameter( required = true ) + private List<String> resourceBundles; + + /** + * Skip remote-resource processing + * + * @since 1.0-alpha-5 + */ + @Parameter( property = "remoteresources.skip", defaultValue = "false" ) + private boolean skip; + + /** + * Attaches the resources to the main build of the project as a resource directory. + * + * @since 1.5 + */ + @Parameter( defaultValue = "true", property = "attachToMain" ) + private boolean attachToMain; + + /** + * Attaches the resources to the test build of the project as a resource directory. + * + * @since 1.5 + */ + @Parameter( defaultValue = "true", property = "attachToTest" ) + private boolean attachToTest; + + /** + * Additional properties to be passed to Velocity. + * Several properties are automatically added:<ul> + * <li><code>project</code> - the current MavenProject </li> + * <li><code>projects</code> - the list of dependency projects</li> + * <li><code>projectsSortedByOrganization</code> - the list of dependency projects sorted by organization</li> + * <li><code>projectTimespan</code> - the timespan of the current project (requires inceptionYear in pom)</li> + * <li><code>locator</code> - the ResourceManager that can be used to retrieve additional resources</li> + * </ul> + * See <a + * href="https://maven.apache.org/ref/current/maven-project/apidocs/org/apache/maven/project/MavenProject.html"> the + * javadoc for MavenProject</a> for information about the properties on the MavenProject. + */ + @Parameter + protected Map<String, Object> properties = new HashMap<>(); + + /** + * Whether to include properties defined in the project when filtering resources. + * + * @since 1.2 + */ + @Parameter( defaultValue = "false" ) + protected boolean includeProjectProperties = false; + + /** + * When the result of velocity transformation fits in memory, it is compared with the actual contents on disk + * to eliminate unnecessary destination file overwrite. This improves build times since further build steps + * typically rely on the modification date. + * + * @since 1.6 + */ + @Parameter( defaultValue = "5242880" ) + protected int velocityFilterInMemoryThreshold = 5 * 1024 * 1024; Review Comment: This is how it is in original code, did not touch this area. ########## src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java: ########## @@ -0,0 +1,1358 @@ +package org.apache.maven.plugin.resources.remote; + +/* + * 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.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.io.output.DeferredFileOutputStream; +import org.apache.maven.RepositoryUtils; +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Model; +import org.apache.maven.model.Organization; +import org.apache.maven.model.Resource; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.resources.remote.io.xpp3.RemoteResourcesBundleXpp3Reader; +import org.apache.maven.plugin.resources.remote.io.xpp3.SupplementalDataModelXpp3Reader; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingResult; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; +import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter; +import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; +import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter; +import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter; +import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.apache.maven.shared.filtering.MavenFileFilterRequest; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.codehaus.plexus.resource.ResourceManager; +import org.codehaus.plexus.resource.loader.FileResourceLoader; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.util.artifact.JavaScopes; + +/** + * <p> + * Pull down resourceBundles containing remote resources and process the resources contained inside. When that is done, + * the resources are injected into the current (in-memory) Maven project, making them available to the process-resources + * phase. + * </p> + * <p> + * Resources that end in ".vm" are treated as Velocity templates. For those, the ".vm" is stripped off for the final + * artifact name and it's fed through Velocity to have properties expanded, conditions processed, etc... + * </p> + * Resources that don't end in ".vm" are copied "as is". + * <p> + * This is a support abstract class, with two non-aggregating and aggregating implementations. + * </p> + */ +public abstract class AbstractProcessRemoteResourcesMojo + extends AbstractMojo +{ + private static final String TEMPLATE_SUFFIX = ".vm"; + + /** + * <p> + * In cases where a local resource overrides one from a remote resource bundle, that resource should be filtered if + * the resource set specifies it. In those cases, this parameter defines the list of delimiters for filterable + * expressions. These delimiters are specified in the form 'beginToken*endToken'. If no '*' 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> + * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can). + * + * @since 1.1 + */ + @Parameter + protected List<String> filterDelimiters; + + /** + * @since 1.1 + */ + @Parameter( defaultValue = "true" ) + protected boolean useDefaultFilterDelimiters; + + /** + * The character encoding scheme to be applied when filtering resources. + */ + @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) + protected String encoding; + + /** + * The directory where processed resources will be placed for packaging. + */ + @Parameter( defaultValue = "${project.build.directory}/maven-shared-archive-resources" ) + private File outputDirectory; + + /** + * The directory containing extra information appended to the generated resources. + */ + @Parameter( defaultValue = "${basedir}/src/main/appended-resources" ) + private File appendedResourcesDirectory; Review Comment: This is how it is in original code, did not touch this area. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@maven.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org