| 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.FileInputStream; |
| import java.io.FileNotFoundException; |
| 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.UnsupportedEncodingException; |
| import java.io.Writer; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.text.SimpleDateFormat; |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| 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.ProjectDependenciesResolver; |
| import org.apache.maven.archiver.MavenArchiver; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.factory.ArtifactFactory; |
| import org.apache.maven.artifact.repository.ArtifactRepository; |
| import org.apache.maven.artifact.resolver.ArtifactNotFoundException; |
| import org.apache.maven.artifact.resolver.ArtifactResolutionException; |
| import org.apache.maven.artifact.resolver.ArtifactResolver; |
| import org.apache.maven.artifact.versioning.VersionRange; |
| 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.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.LifecyclePhase; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.project.InvalidProjectModelException; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.MavenProjectBuilder; |
| import org.apache.maven.project.ProjectBuildingException; |
| import org.apache.maven.project.artifact.InvalidDependencyVersionException; |
| import org.apache.maven.project.inheritance.ModelInheritanceAssembler; |
| 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.RuntimeConstants; |
| import org.apache.velocity.runtime.RuntimeServices; |
| import org.apache.velocity.runtime.log.LogChute; |
| 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; |
| |
| /** |
| * <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> |
| * <p/> |
| * Resources that don't end in ".vm" are copied "as is". |
| */ |
| // NOTE: Removed the following in favor of maven-artifact-resolver library, for MRRESOURCES-41 |
| // If I leave this intact, interdependent projects within the reactor that haven't been built |
| // (remember, this runs in the generate-resources phase) will cause the build to fail. |
| // |
| // @requiresDependencyResolution test |
| @Mojo( name = "process", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true ) |
| public class ProcessRemoteResourcesMojo |
| extends AbstractMojo |
| implements LogChute |
| { |
| |
| 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> |
| * <p/> |
| * 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; |
| |
| /** |
| * If true, only generate resources in the directory of the root project in a multimodule build. |
| * Dependencies from all modules will be aggregated before resource-generation takes place. |
| * |
| * @since 1.1 |
| */ |
| @Parameter( defaultValue = "false" ) |
| protected boolean runOnlyAtExecutionRoot; |
| |
| /** |
| * Used for calculation of execution-root for {@link ProcessRemoteResourcesMojo#runOnlyAtExecutionRoot}. |
| */ |
| @Parameter( defaultValue = "${basedir}", readonly = true, required = true ) |
| protected File basedir; |
| |
| /** |
| * The character encoding scheme to be applied when filtering resources. |
| */ |
| @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) |
| protected String encoding; |
| |
| /** |
| * The local repository taken from Maven's runtime. Typically <code>$HOME/.m2/repository</code>. |
| */ |
| @Parameter( defaultValue = "${localRepository}", readonly = true, required = true ) |
| private ArtifactRepository localRepository; |
| |
| /** |
| * List of Remote Repositories used by the resolver. |
| */ |
| @Parameter( defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true ) |
| private List<ArtifactRepository> remoteArtifactRepositories; |
| |
| /** |
| * The current Maven project. |
| */ |
| @Parameter( defaultValue = "${project}", readonly = true, required = true ) |
| private MavenProject project; |
| |
| /** |
| * 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; |
| |
| /** |
| * 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. |
| */ |
| @Component |
| private ModelInheritanceAssembler inheritanceAssembler; |
| |
| /** |
| * 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. |
| * <p/> |
| * 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 |
| private 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 list of resources defined for the project. |
| */ |
| @Parameter( defaultValue = "${project.resources}", readonly = true, required = true ) |
| private List<Resource> resources; |
| |
| /** |
| * Artifact Resolver, needed to resolve and download the {@code resourceBundles}. |
| */ |
| @Component |
| private ArtifactResolver artifactResolver; |
| |
| /** |
| * Filtering support, for local resources that override those in the remote bundle. |
| */ |
| @Component |
| private MavenFileFilter fileFilter; |
| |
| /** |
| * Artifact factory, needed to create artifacts. |
| */ |
| @Component |
| private ArtifactFactory artifactFactory; |
| |
| /** |
| * The Maven session. |
| */ |
| @Parameter( defaultValue = "${session}", readonly = true, required = true ) |
| private MavenSession mavenSession; |
| |
| /** |
| * ProjectBuilder, needed to create projects from the artifacts. |
| */ |
| @Component( role = MavenProjectBuilder.class ) |
| private MavenProjectBuilder mavenProjectBuilder; |
| |
| /** |
| */ |
| @Component |
| private ResourceManager locator; |
| |
| /** |
| * 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 |
| private String[] resolveScopes; |
| |
| /** |
| * Comma separated list of Artifact names too 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( hint = "default" ) |
| protected ProjectDependenciesResolver dependencyResolver; |
| |
| private VelocityEngine velocity; |
| |
| @Override |
| @SuppressWarnings( "unchecked" ) |
| 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 ( runOnlyAtExecutionRoot && !project.isExecutionRoot() ) |
| { |
| getLog().info( "Skipping remote-resource generation in this project because it's not the Execution Root" ); |
| return; |
| } |
| |
| if ( resolveScopes == null ) |
| { |
| resolveScopes = |
| new String[] { StringUtils.isEmpty( excludeScope ) ? this.includeScope : Artifact.SCOPE_TEST }; |
| } |
| |
| 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.toString() ); |
| } |
| } |
| } |
| |
| 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( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this ); |
| velocity.setProperty( "resource.loader", "classpath" ); |
| velocity.setProperty( "classpath.resource.loader.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() ) ); |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| 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 = resolveProjectArtifacts(); |
| if ( this.excludeTransitive ) |
| { |
| Set<Artifact> depArtifacts; |
| if ( runOnlyAtExecutionRoot ) |
| { |
| depArtifacts = aggregateProjectDependencyArtifacts(); |
| } |
| else |
| { |
| depArtifacts = project.getDependencyArtifacts(); |
| } |
| filter.addFilter( new ProjectTransitivityFilter( depArtifacts, 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 ); |
| } |
| |
| for ( Artifact artifact : artifacts ) |
| { |
| try |
| { |
| List<ArtifactRepository> remoteRepo = remoteArtifactRepositories; |
| if ( artifact.isSnapshot() ) |
| { |
| VersionRange rng = VersionRange.createFromVersion( artifact.getBaseVersion() ); |
| artifact = |
| artifactFactory.createDependencyArtifact( artifact.getGroupId(), artifact.getArtifactId(), rng, |
| artifact.getType(), artifact.getClassifier(), |
| artifact.getScope(), null, artifact.isOptional() ); |
| } |
| |
| getLog().debug( "Building project for " + artifact ); |
| MavenProject p; |
| try |
| { |
| p = mavenProjectBuilder.buildFromRepository( artifact, remoteRepo, localRepository ); |
| } |
| catch ( InvalidProjectModelException e ) |
| { |
| getLog().warn( "Invalid project model for artifact [" + artifact.getArtifactId() + ":" |
| + artifact.getGroupId() + ":" + 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() + "]" ); |
| } |
| } |
| catch ( ProjectBuildingException e ) |
| { |
| throw new IllegalStateException( e.getMessage(), e ); |
| } |
| } |
| Collections.sort( projects, new ProjectComparator() ); |
| return projects; |
| } |
| |
| private Set<Artifact> resolveProjectArtifacts() |
| { |
| try |
| { |
| if ( runOnlyAtExecutionRoot ) |
| { |
| List<MavenProject> projects = mavenSession.getSortedProjects(); |
| return dependencyResolver.resolve( projects, Arrays.asList( resolveScopes ), mavenSession ); |
| } |
| else |
| { |
| return dependencyResolver.resolve( project, Arrays.asList( resolveScopes ), mavenSession ); |
| } |
| } |
| catch ( ArtifactResolutionException e ) |
| { |
| throw new IllegalStateException( "Failed to resolve dependencies for one or more projects in the reactor. " |
| + "Reason: " + e.getMessage(), e ); |
| } |
| catch ( ArtifactNotFoundException e ) |
| { |
| throw new IllegalStateException( "Failed to resolve dependencies for one or more projects in the reactor. " |
| + "Reason: " + e.getMessage(), e ); |
| } |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private Set<Artifact> aggregateProjectDependencyArtifacts() |
| { |
| Set<Artifact> artifacts = new LinkedHashSet<>(); |
| |
| List<MavenProject> projects = mavenSession.getSortedProjects(); |
| for ( MavenProject p : projects ) |
| { |
| if ( p.getDependencyArtifacts() == null ) |
| { |
| try |
| { |
| Set<Artifact> depArtifacts = p.createArtifacts( artifactFactory, null, null ); |
| |
| if ( depArtifacts != null && !depArtifacts.isEmpty() ) |
| { |
| for ( Artifact artifact : depArtifacts ) |
| { |
| if ( artifact.getVersion() == null && artifact.getVersionRange() != null ) |
| { |
| // Version is required for equality comparison between artifacts, |
| // but it is not needed for our purposes of filtering out |
| // transitive dependencies (which requires only groupId/artifactId). |
| // Therefore set an arbitrary version if missing. |
| artifact.setResolvedVersion( Artifact.LATEST_VERSION ); |
| } |
| artifacts.add( artifact ); |
| } |
| } |
| } |
| catch ( InvalidDependencyVersionException e ) |
| { |
| throw new IllegalStateException( "Failed to create dependency artifacts for: " + p.getId() |
| + ". Reason: " + e.getMessage(), e ); |
| } |
| } |
| } |
| |
| return artifacts; |
| } |
| |
| 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 : resources ) |
| { |
| 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( new FileInputStream( source ), 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 org.apache.commons.io.output.DeferredFileOutputStream#close} |
| * |
| * @param outStream Deferred stream |
| * @throws IOException |
| */ |
| 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 = new FileInputStream( file ) ) |
| { |
| final 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 = new FileOutputStream( file ) ) |
| { |
| 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 ) |
| throws MojoExecutionException |
| { |
| // 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<>(); |
| |
| try |
| { |
| 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.getSortedProjects(); |
| 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 type = ( s.length >= 4 ? s[3] : "jar" ); |
| String classifier = ( s.length == 5 ? s[4] : null ); |
| Artifact artifact = |
| artifactFactory.createDependencyArtifact( s[0], s[1], VersionRange.createFromVersion( s[2] ), |
| type, classifier, Artifact.SCOPE_RUNTIME ); |
| |
| artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); |
| |
| artifactFile = artifact.getFile(); |
| } |
| bundleArtifacts.add( artifactFile ); |
| } |
| } |
| catch ( ArtifactResolutionException e ) |
| { |
| throw new MojoExecutionException( "Error downloading resources archive.", e ); |
| } |
| catch ( ArtifactNotFoundException e ) |
| { |
| throw new MojoExecutionException( "Resources archive cannot be found.", e ); |
| } |
| |
| 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 ); |
| } |
| } |
| |
| protected void processResourceBundles( ClassLoader classLoader, VelocityContext context ) |
| throws MojoExecutionException |
| { |
| List<Map.Entry<String, RemoteResourcesBundle>> remoteResources = |
| new ArrayList<>(); |
| int bundleCount = 0; |
| int resourceCount = 0; |
| |
| // list remote resources form bundles |
| try |
| { |
| RemoteResourcesBundleXpp3Reader bundleReader = new RemoteResourcesBundleXpp3Reader(); |
| |
| for ( Enumeration<URL> e = |
| classLoader.getResources( BundleRemoteResourcesMojo.RESOURCES_MANIFEST ); e.hasMoreElements(); ) |
| { |
| URL url = e.nextElement(); |
| bundleCount++; |
| getLog().debug( "processResourceBundle on bundle#" + bundleCount + " " + url ); |
| |
| RemoteResourcesBundle bundle; |
| |
| try ( InputStream in = url.openStream() ) |
| { |
| bundle = bundleReader.read( in ); |
| } |
| |
| int n = 0; |
| for ( String bundleResource : bundle.getRemoteResources() ) |
| { |
| n++; |
| resourceCount++; |
| getLog().debug( "bundle#" + bundleCount + " resource#" + n + " " + bundleResource ); |
| remoteResources.add( new AbstractMap.SimpleEntry<>( bundleResource, |
| bundle ) ); |
| } |
| } |
| } |
| catch ( IOException ioe ) |
| { |
| throw new MojoExecutionException( "Error finding remote resources manifests", ioe ); |
| } |
| catch ( XmlPullParserException xppe ) |
| { |
| throw new MojoExecutionException( "Error parsing remote resource bundle descriptor.", xppe ); |
| } |
| |
| getLog().info( "Copying " + resourceCount + " resource" + ( ( resourceCount > 1 ) ? "s" : "" ) + " from " |
| + bundleCount + " bundle" + ( ( bundleCount > 1 ) ? "s" : "" ) + "." ); |
| |
| String velocityResource = null; |
| try |
| { |
| |
| for ( Map.Entry<String, RemoteResourcesBundle> entry : remoteResources ) |
| { |
| String bundleResource = entry.getKey(); |
| RemoteResourcesBundle bundle = entry.getValue(); |
| |
| String projectResource = bundleResource; |
| |
| boolean doVelocity = false; |
| if ( projectResource.endsWith( TEMPLATE_SUFFIX ) ) |
| { |
| projectResource = projectResource.substring( 0, projectResource.length() - 3 ); |
| velocityResource = bundleResource; |
| doVelocity = true; |
| } |
| |
| // Don't overwrite resource that are already being provided. |
| |
| File f = new File( outputDirectory, projectResource ); |
| |
| FileUtils.mkdir( f.getParentFile().getAbsolutePath() ); |
| |
| if ( !copyResourceIfExists( f, projectResource, context ) ) |
| { |
| if ( doVelocity ) |
| { |
| try ( DeferredFileOutputStream os = |
| new DeferredFileOutputStream( velocityFilterInMemoryThreshold, f ) ) |
| { |
| try ( Writer writer = bundle.getSourceEncoding() == null ? new OutputStreamWriter( os ) |
| : new OutputStreamWriter( os, bundle.getSourceEncoding() ) ) |
| { |
| if ( bundle.getSourceEncoding() == null ) |
| { |
| // TODO: Is this correct? Shouldn't we behave like the rest of maven and fail |
| // down to JVM default instead ISO-8859-1 ? |
| velocity.mergeTemplate( bundleResource, "ISO-8859-1", context, writer ); |
| } |
| else |
| { |
| velocity.mergeTemplate( bundleResource, bundle.getSourceEncoding(), context, |
| writer ); |
| } |
| } |
| fileWriteIfDiffers( os ); |
| } |
| } |
| else |
| { |
| URL resUrl = classLoader.getResource( bundleResource ); |
| if ( resUrl != null ) |
| { |
| FileUtils.copyURLToFile( resUrl, f ); |
| } |
| } |
| |
| File appendedResourceFile = new File( appendedResourcesDirectory, projectResource ); |
| File appendedVmResourceFile = new File( appendedResourcesDirectory, projectResource + ".vm" ); |
| |
| if ( appendedResourceFile.exists() ) |
| { |
| getLog().info( "Copying appended resource: " + projectResource ); |
| try ( InputStream in = new FileInputStream( appendedResourceFile ); |
| OutputStream out = new FileOutputStream( f, true ) ) |
| { |
| IOUtil.copy( in, out ); |
| } |
| |
| } |
| else if ( appendedVmResourceFile.exists() ) |
| { |
| getLog().info( "Filtering appended resource: " + projectResource + ".vm" ); |
| |
| |
| try ( Reader reader = new FileReader( appendedVmResourceFile ); |
| Writer writer = getWriter( bundle, f ) ) |
| { |
| Velocity.init(); |
| Velocity.evaluate( context, writer, "remote-resources", reader ); |
| } |
| } |
| } |
| } |
| } |
| catch ( IOException ioe ) |
| { |
| throw new MojoExecutionException( "Error reading remote resource", ioe ); |
| } |
| catch ( VelocityException e ) |
| { |
| throw new MojoExecutionException( "Error rendering Velocity resource '" + velocityResource + "'", e ); |
| } |
| } |
| |
| private Writer getWriter( RemoteResourcesBundle bundle, File f ) |
| throws IOException, UnsupportedEncodingException, FileNotFoundException |
| { |
| Writer writer; |
| if ( bundle.getSourceEncoding() == null ) |
| { |
| writer = new PrintWriter( new FileWriter( f, true ) ); |
| } |
| else |
| { |
| writer = new PrintWriter( new OutputStreamWriter( new FileOutputStream( f, true ), |
| bundle.getSourceEncoding() ) ); |
| } |
| return writer; |
| } |
| |
| protected Model getSupplement( Xpp3Dom supplementModelXml ) |
| throws MojoExecutionException |
| { |
| MavenXpp3Reader modelReader = new MavenXpp3Reader(); |
| Model model = null; |
| |
| try |
| { |
| model = modelReader.read( new StringReader( supplementModelXml.toString() ) ); |
| String groupId = model.getGroupId(); |
| String artifactId = model.getArtifactId(); |
| |
| if ( groupId == null || groupId.trim().equals( "" ) ) |
| { |
| throw new MojoExecutionException( "Supplemental project XML " |
| + "requires that a <groupId> element be present." ); |
| } |
| |
| if ( artifactId == null || artifactId.trim().equals( "" ) ) |
| { |
| throw new MojoExecutionException( "Supplemental project XML " |
| + "requires that a <artifactId> element be present." ); |
| } |
| } |
| catch ( IOException e ) |
| { |
| getLog().warn( "Unable to read supplemental XML: " + e.getMessage(), e ); |
| } |
| catch ( XmlPullParserException e ) |
| { |
| getLog().warn( "Unable to parse supplemental XML: " + e.getMessage(), e ); |
| } |
| |
| return model; |
| } |
| |
| protected Model mergeModels( Model parent, Model child ) |
| { |
| inheritanceAssembler.assembleModelInheritance( child, parent ); |
| return child; |
| } |
| |
| private static String generateSupplementMapKey( String groupId, String artifactId ) |
| { |
| return groupId.trim() + ":" + artifactId.trim(); |
| } |
| |
| private Map<String, Model> loadSupplements( String models[] ) |
| throws MojoExecutionException |
| { |
| if ( models == null ) |
| { |
| getLog().debug( "Supplemental data models won't be loaded. " + "No models specified." ); |
| return Collections.emptyMap(); |
| } |
| |
| List<Supplement> supplements = new ArrayList<>(); |
| for ( String set : models ) |
| { |
| getLog().debug( "Preparing ruleset: " + set ); |
| try |
| { |
| File f = locator.getResourceAsFile( set, getLocationTemp( set ) ); |
| |
| if ( null == f || !f.exists() ) |
| { |
| throw new MojoExecutionException( "Cold not resolve " + set ); |
| } |
| if ( !f.canRead() ) |
| { |
| throw new MojoExecutionException( "Supplemental data models won't be loaded. " + "File " |
| + f.getAbsolutePath() + " cannot be read, check permissions on the file." ); |
| } |
| |
| getLog().debug( "Loading supplemental models from " + f.getAbsolutePath() ); |
| |
| SupplementalDataModelXpp3Reader reader = new SupplementalDataModelXpp3Reader(); |
| SupplementalDataModel supplementalModel = reader.read( new FileReader( f ) ); |
| supplements.addAll( supplementalModel.getSupplement() ); |
| } |
| catch ( Exception e ) |
| { |
| String msg = "Error loading supplemental data models: " + e.getMessage(); |
| getLog().error( msg, e ); |
| throw new MojoExecutionException( msg, e ); |
| } |
| } |
| |
| getLog().debug( "Loading supplements complete." ); |
| |
| Map<String, Model> supplementMap = new HashMap<>(); |
| for ( Supplement sd : supplements ) |
| { |
| Xpp3Dom dom = (Xpp3Dom) sd.getProject(); |
| |
| Model m = getSupplement( dom ); |
| supplementMap.put( generateSupplementMapKey( m.getGroupId(), m.getArtifactId() ), m ); |
| } |
| |
| return supplementMap; |
| } |
| |
| /** |
| * Convenience method to get the location of the specified file name. |
| * |
| * @param name the name of the file whose location is to be resolved |
| * @return a String that contains the absolute file name of the file |
| */ |
| private String getLocationTemp( String name ) |
| { |
| String loc = name; |
| if ( loc.indexOf( '/' ) != -1 ) |
| { |
| loc = loc.substring( loc.lastIndexOf( '/' ) + 1 ); |
| } |
| if ( loc.indexOf( '\\' ) != -1 ) |
| { |
| loc = loc.substring( loc.lastIndexOf( '\\' ) + 1 ); |
| } |
| getLog().debug( "Before: " + name + " After: " + loc ); |
| return loc; |
| } |
| |
| class OrganizationComparator |
| implements Comparator<Organization> |
| { |
| @Override |
| public int compare( Organization org1, Organization org2 ) |
| { |
| int i = compareStrings( org1.getName(), org2.getName() ); |
| if ( i == 0 ) |
| { |
| i = compareStrings( org1.getUrl(), org2.getUrl() ); |
| } |
| return i; |
| } |
| |
| public boolean equals( Organization o1, Organization o2 ) |
| { |
| return compare( o1, o2 ) == 0; |
| } |
| |
| private int compareStrings( String s1, String s2 ) |
| { |
| if ( s1 == null && s2 == null ) |
| { |
| return 0; |
| } |
| else if ( s1 == null && s2 != null ) |
| { |
| return 1; |
| } |
| else if ( s1 != null && s2 == null ) |
| { |
| return -1; |
| } |
| |
| return s1.compareToIgnoreCase( s2 ); |
| } |
| } |
| |
| class ProjectComparator |
| implements Comparator<MavenProject> |
| { |
| @Override |
| public int compare( MavenProject p1, MavenProject p2 ) |
| { |
| return p1.getArtifact().compareTo( p2.getArtifact() ); |
| } |
| |
| public boolean equals( MavenProject p1, MavenProject p2 ) |
| { |
| return p1.getArtifact().equals( p2.getArtifact() ); |
| } |
| } |
| |
| /* LogChute methods */ |
| @Override |
| public void init( RuntimeServices rs ) |
| throws Exception |
| { |
| } |
| |
| @Override |
| public void log( int level, String message ) |
| { |
| switch ( level ) |
| { |
| case LogChute.WARN_ID: |
| getLog().warn( message ); |
| break; |
| case LogChute.INFO_ID: |
| // velocity info messages are too verbose, just consider them as debug messages... |
| getLog().debug( message ); |
| break; |
| case LogChute.DEBUG_ID: |
| getLog().debug( message ); |
| break; |
| case LogChute.ERROR_ID: |
| getLog().error( message ); |
| break; |
| default: |
| getLog().debug( message ); |
| break; |
| } |
| } |
| |
| @Override |
| public void log( int level, String message, Throwable t ) |
| { |
| switch ( level ) |
| { |
| case LogChute.WARN_ID: |
| getLog().warn( message, t ); |
| break; |
| case LogChute.INFO_ID: |
| // velocity info messages are too verbose, just consider them as debug messages... |
| getLog().debug( message, t ); |
| break; |
| case LogChute.DEBUG_ID: |
| getLog().debug( message, t ); |
| break; |
| case LogChute.ERROR_ID: |
| getLog().error( message, t ); |
| break; |
| default: |
| getLog().debug( message, t ); |
| break; |
| } |
| } |
| |
| @Override |
| public boolean isLevelEnabled( int level ) |
| { |
| return false; |
| } |
| |
| } |