From 37afe816a24358f4461e508d2ed22a370476316c Mon Sep 17 00:00:00 2001 From: rfscholte Date: Mon, 21 Dec 2020 22:23:43 +0100 Subject: [PATCH] [MNG-6957] Versionless reactor dependencies/parent should work even if modules are aggregated in reverse order This closes #391 --- .../ConsumerModelSourceTransformer.java | 4 +- .../maven/project/DefaultProjectBuilder.java | 122 ++-- .../maven/project/ReactorModelCache.java | 9 +- .../DefaultConsumerPomXMLFilterFactory.java | 29 +- .../building/BuildModelSourceTransformer.java | 2 +- .../DefaultBuildPomXMLFilterFactory.java | 45 +- .../model/building/DefaultModelBuilder.java | 640 ++++++++++++------ .../building/DefaultModelBuildingRequest.java | 21 +- .../building/DefaultModelBuildingResult.java | 30 + .../building/DefaultTransformerContext.java | 95 +++ .../building/FilterModelBuildingRequest.java | 12 +- .../maven/model/building/ModelBuilder.java | 2 + .../model/building/ModelBuildingRequest.java | 32 +- .../model/building/ModelBuildingResult.java | 7 + .../maven/model/building/ModelCacheTag.java | 10 +- .../maven/model/building/ModelData.java | 113 +--- .../building/ModelSourceTransformer.java | 14 +- .../model/building/TransformerContext.java | 6 +- .../building/TransformerContextBuilder.java | 46 ++ .../maven/model/io/DefaultModelReader.java | 14 +- .../apache/maven/model/io/ModelReader.java | 6 + maven-model-builder/src/site/apt/index.apt | 27 +- .../validation/DefaultModelValidatorTest.java | 4 - .../sax/filter/BuildPomXMLFilterFactory.java | 45 +- .../xml/sax/filter/CiFriendlyXMLFilter.java | 31 +- .../filter/ConsumerPomXMLFilterFactory.java | 33 +- .../maven/xml/sax/filter/ParentXMLFilter.java | 71 +- .../sax/filter/CiFriendlyXMLFilterTest.java | 2 +- .../sax/filter/ConsumerPomXMLFilterTest.java | 17 +- .../xml/sax/filter/ParentXMLFilterTest.java | 37 +- 30 files changed, 934 insertions(+), 592 deletions(-) create mode 100644 maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java create mode 100644 maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContextBuilder.java diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java b/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java index d81571da3486..4d5d2636532c 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java +++ b/maven-core/src/main/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformer.java @@ -46,13 +46,13 @@ class ConsumerModelSourceTransformer extends AbstractModelSourceTransformer { @Override - protected AbstractSAXFilter getSAXFilter( Path pomFile, + protected AbstractSAXFilter getSAXFilter( Path pomFile, TransformerContext context, Consumer lexicalHandlerConsumer ) throws TransformerConfigurationException, SAXException, ParserConfigurationException { return new DefaultConsumerPomXMLFilterFactory( new DefaultBuildPomXMLFilterFactory( context, - lexicalHandlerConsumer ) ).get( pomFile ); + lexicalHandlerConsumer, true ) ).get( pomFile ); } /** diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java index 624f6ade9d90..308097d3dc8e 100644 --- a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java @@ -21,7 +21,6 @@ import java.io.File; import java.io.IOException; -import java.nio.file.Path; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; @@ -33,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; @@ -56,6 +56,7 @@ import org.apache.maven.model.Profile; import org.apache.maven.model.ReportPlugin; import org.apache.maven.model.building.ArtifactModelSource; +import org.apache.maven.model.building.TransformerContextBuilder; import org.apache.maven.model.building.DefaultModelBuildingRequest; import org.apache.maven.model.building.DefaultModelProblem; import org.apache.maven.model.building.FileModelSource; @@ -129,7 +130,7 @@ public ProjectBuildingResult build( File pomFile, ProjectBuildingRequest request throws ProjectBuildingException { return build( pomFile, new FileModelSource( pomFile ), - new InternalConfig( request, null, useGlobalModelCache() ? getModelCache() : null ) ); + new InternalConfig( request, null, useGlobalModelCache() ? getModelCache() : null, null ) ); } private boolean useGlobalModelCache() @@ -142,7 +143,7 @@ public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequ throws ProjectBuildingException { return build( null, modelSource, - new InternalConfig( request, null, useGlobalModelCache() ? getModelCache() : null ) ); + new InternalConfig( request, null, useGlobalModelCache() ? getModelCache() : null, null ) ); } private ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConfig config ) @@ -263,14 +264,7 @@ private DependencyResolutionResult resolveDependencies( MavenProject project, Re private List getProfileIds( List profiles ) { - List ids = new ArrayList<>( profiles.size() ); - - for ( Profile profile : profiles ) - { - ids.add( profile.getId() ); - } - - return ids; + return profiles.stream().map( Profile::getId ).collect( Collectors.toList() ); } private ModelBuildingRequest getModelBuildingRequest( InternalConfig config ) @@ -295,7 +289,7 @@ private ModelBuildingRequest getModelBuildingRequest( InternalConfig config ) request.setBuildStartTime( configuration.getBuildStartTime() ); request.setModelResolver( resolver ); request.setModelCache( config.modelCache ); - request.setTransformerContext( (TransformerContext) config.session.getData().get( TransformerContext.KEY ) ); + request.setTransformerContextBuilder( config.transformerContextBuilder ); return request; } @@ -314,7 +308,8 @@ public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, P org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact ); pomArtifact = ArtifactDescriptorUtils.toPomArtifact( pomArtifact ); - InternalConfig config = new InternalConfig( request, null, useGlobalModelCache() ? getModelCache() : null ); + InternalConfig config = + new InternalConfig( request, null, useGlobalModelCache() ? getModelCache() : null, null ); boolean localProject; @@ -385,37 +380,14 @@ public List build( List pomFiles, boolean recursive ReactorModelPool.Builder poolBuilder = new ReactorModelPool.Builder(); final ReactorModelPool modelPool = poolBuilder.build(); - - if ( Features.buildConsumer().isActive() ) - { - final TransformerContext context = new TransformerContext() - { - @Override - public String getUserProperty( String key ) - { - return request.getUserProperties().getProperty( key ); - } - - @Override - public Model getRawModel( Path p ) - { - return modelPool.get( p ); - } - - @Override - public Model getRawModel( String groupId, String artifactId ) - { - return modelPool.get( groupId, artifactId, null ); - } - }; - request.getRepositorySession().getData().set( TransformerContext.KEY, context ); - } - InternalConfig config = new InternalConfig( request, modelPool, - useGlobalModelCache() ? getModelCache() : new ReactorModelCache() ); + InternalConfig config = + new InternalConfig( request, modelPool, useGlobalModelCache() ? getModelCache() : new ReactorModelCache(), + modelBuilder.newTransformerContextBuilder() ); - Map projectIndex = new HashMap<>( 256 ); + Map projectIndex = new HashMap<>( 256 ); + // phase 1: get file Models from the reactor. boolean noErrors = build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive, config, poolBuilder ); @@ -424,6 +396,7 @@ public Model getRawModel( String groupId, String artifactId ) try { + // Phase 2: get effective models from the reactor noErrors = build( results, new ArrayList<>(), projectIndex, interimResults, request, new HashMap<>(), config.session ) && noErrors; @@ -433,6 +406,12 @@ public Model getRawModel( String groupId, String artifactId ) Thread.currentThread().setContextClassLoader( oldContextClassLoader ); } + if ( Features.buildConsumer().isActive() ) + { + request.getRepositorySession().getData().set( TransformerContext.KEY, + config.transformerContextBuilder.build() ); + } + if ( !noErrors ) { throw new ProjectBuildingException( results ); @@ -443,7 +422,7 @@ public Model getRawModel( String groupId, String artifactId ) @SuppressWarnings( "checkstyle:parameternumber" ) private boolean build( List results, List interimResults, - Map projectIndex, List pomFiles, Set aggregatorFiles, + Map projectIndex, List pomFiles, Set aggregatorFiles, boolean root, boolean recursive, InternalConfig config, ReactorModelPool.Builder poolBuilder ) { @@ -467,20 +446,19 @@ private boolean build( List results, List @SuppressWarnings( "checkstyle:parameternumber" ) private boolean build( List results, List interimResults, - Map projectIndex, File pomFile, Set aggregatorFiles, + Map projectIndex, File pomFile, Set aggregatorFiles, boolean isRoot, boolean recursive, InternalConfig config, ReactorModelPool.Builder poolBuilder ) { boolean noErrors = true; - ModelBuildingRequest request = getModelBuildingRequest( config ); - MavenProject project = new MavenProject(); project.setFile( pomFile ); - request.setPomFile( pomFile ); - request.setTwoPhaseBuilding( true ); - request.setLocationTracking( true ); + ModelBuildingRequest request = getModelBuildingRequest( config ) + .setPomFile( pomFile ) + .setTwoPhaseBuilding( true ) + .setLocationTracking( true ); DefaultModelBuildingListener listener = new DefaultModelBuildingListener( project, projectBuildingHelper, config.request ); @@ -494,7 +472,7 @@ private boolean build( List results, List catch ( ModelBuildingException e ) { result = e.getResult(); - if ( result == null || result.getEffectiveModel() == null ) + if ( result == null || result.getFileModel() == null ) { results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); @@ -505,32 +483,17 @@ private boolean build( List results, List noErrors = false; } - Model model = result.getEffectiveModel(); - - poolBuilder.put( model.getPomFile().toPath(), result.getRawModel() ); - - try - { - // first pass: build without building parent. - initProject( project, projectIndex, false, result, new HashMap<>( 0 ), config.request ); - } - catch ( InvalidArtifactRTException iarte ) - { - result.getProblems().add( new DefaultModelProblem( null, ModelProblem.Severity.ERROR, null, model, -1, -1, - iarte ) ); - } + Model model = result.getFileModel().clone(); - projectIndex.put( result.getModelIds().get( 0 ), project ); + poolBuilder.put( model.getPomFile().toPath(), model ); InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot ); interimResults.add( interimResult ); - if ( recursive && !model.getModules().isEmpty() ) + if ( recursive ) { File basedir = pomFile.getParentFile(); - List moduleFiles = new ArrayList<>(); - for ( String module : model.getModules() ) { if ( StringUtils.isEmpty( module ) ) @@ -609,6 +572,8 @@ private boolean build( List results, List } } + projectIndex.put( pomFile, project ); + return noErrors; } @@ -640,7 +605,7 @@ static class InterimResult } private boolean build( List results, List projects, - Map projectIndex, List interimResults, + Map projectIndex, List interimResults, ProjectBuildingRequest request, Map profilesXmls, RepositorySystemSession session ) { @@ -685,12 +650,14 @@ private boolean build( List results, List p catch ( ModelBuildingException e ) { DefaultProjectBuildingResult result = null; - if ( project == null ) + if ( project == null || interimResult.result.getEffectiveModel() == null ) { result = new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ); } else { + project.setModel( interimResult.result.getEffectiveModel() ); + result = new DefaultProjectBuildingResult( project, e.getProblems(), null ); } results.add( result ); @@ -703,15 +670,14 @@ private boolean build( List results, List p } @SuppressWarnings( "checkstyle:methodlength" ) - private void initProject( MavenProject project, Map projects, + private void initProject( MavenProject project, Map projects, boolean buildParentIfNotExisting, ModelBuildingResult result, Map profilesXmls, ProjectBuildingRequest projectBuildingRequest ) { Model model = result.getEffectiveModel(); project.setModel( model ); - project.setOriginalModel( result.getRawModel() ); - project.setFile( model.getPomFile() ); + project.setOriginalModel( result.getFileModel() ); initParent( project, projects, buildParentIfNotExisting, result, projectBuildingRequest ); @@ -938,7 +904,7 @@ HashMap compute() } } - private void initParent( MavenProject project, Map projects, boolean buildParentIfNotExisting, + private void initParent( MavenProject project, Map projects, boolean buildParentIfNotExisting, ModelBuildingResult result, ProjectBuildingRequest projectBuildingRequest ) { Model parentModel = result.getModelIds().size() > 1 && !result.getModelIds().get( 1 ).isEmpty() @@ -957,7 +923,7 @@ private void initParent( MavenProject project, Map project // org.apache.maven.its.mng4834:parent:0.1 String parentModelId = result.getModelIds().get( 1 ); File parentPomFile = result.getRawModel( parentModelId ).getPomFile(); - MavenProject parent = projects.get( parentModelId ); + MavenProject parent = projects.get( parentPomFile ); if ( parent == null && buildParentIfNotExisting ) { // @@ -1096,15 +1062,21 @@ class InternalConfig private final ReactorModelCache modelCache; - InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, ReactorModelCache modelCache ) + private final TransformerContextBuilder transformerContextBuilder; + + InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, ReactorModelCache modelCache, + TransformerContextBuilder transformerContextBuilder ) { this.request = request; this.modelPool = modelPool; this.modelCache = modelCache; + this.transformerContextBuilder = transformerContextBuilder; + session = LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(), repoSystem ); repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); + } } diff --git a/maven-core/src/main/java/org/apache/maven/project/ReactorModelCache.java b/maven-core/src/main/java/org/apache/maven/project/ReactorModelCache.java index 72c25c79f888..20e57b9eca6d 100644 --- a/maven-core/src/main/java/org/apache/maven/project/ReactorModelCache.java +++ b/maven-core/src/main/java/org/apache/maven/project/ReactorModelCache.java @@ -80,13 +80,7 @@ private static final class GavCacheKey this.artifactId = ( artifactId != null ) ? artifactId : ""; this.version = ( version != null ) ? version : ""; this.tag = ( tag != null ) ? tag : ""; - - int hash = 17; - hash = hash * 31 + this.groupId.hashCode(); - hash = hash * 31 + this.artifactId.hashCode(); - hash = hash * 31 + this.version.hashCode(); - hash = hash * 31 + this.tag.hashCode(); - hashCode = hash; + this.hashCode = Objects.hash( groupId, artifactId, version, tag ); } @Override @@ -113,7 +107,6 @@ public int hashCode() { return hashCode; } - } private static final class SourceCacheKey diff --git a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java b/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java index aa68680279c5..b27a87b3fef5 100644 --- a/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java +++ b/maven-core/src/main/java/org/apache/maven/xml/internal/DefaultConsumerPomXMLFilterFactory.java @@ -9,7 +9,7 @@ * "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 + * 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 @@ -19,10 +19,7 @@ * under the License. */ -import java.util.Optional; - import org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory; -import org.apache.maven.model.building.TransformerContext; import org.apache.maven.xml.sax.filter.ConsumerPomXMLFilterFactory; /** @@ -34,32 +31,8 @@ */ public class DefaultConsumerPomXMLFilterFactory extends ConsumerPomXMLFilterFactory { - private final TransformerContext context; - public DefaultConsumerPomXMLFilterFactory( DefaultBuildPomXMLFilterFactory buildPomXMLFilterFactory ) { super( buildPomXMLFilterFactory ); - this.context = buildPomXMLFilterFactory.getContext(); - } - - @Override - protected Optional getChangelist() - { - return Optional.ofNullable( context.getUserProperty( "changelist" ) ); - } - - @Override - protected Optional getRevision() - { - return Optional.ofNullable( context.getUserProperty( "revision" ) ); } - - @Override - protected Optional getSha1() - { - return Optional.ofNullable( context.getUserProperty( "sha1" ) ); - } - - - } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java index 9766b8d76a5c..11196234eeda 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/BuildModelSourceTransformer.java @@ -58,7 +58,7 @@ protected AbstractSAXFilter getSAXFilter( Path pomFile, throws TransformerConfigurationException, SAXException, ParserConfigurationException { BuildPomXMLFilterFactory buildPomXMLFilterFactory = - new DefaultBuildPomXMLFilterFactory( context, lexicalHandlerConsumer ); + new DefaultBuildPomXMLFilterFactory( context, lexicalHandlerConsumer, false ); return buildPomXMLFilterFactory.get( pomFile ); } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java index cf095e063875..2424874d22ec 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultBuildPomXMLFilterFactory.java @@ -32,32 +32,35 @@ import org.xml.sax.ext.LexicalHandler; /** - * + * A BuildPomXMLFilterFactory which is context aware + * * @author Robert Scholte * @since 4.0.0 */ public class DefaultBuildPomXMLFilterFactory extends BuildPomXMLFilterFactory { private final TransformerContext context; - + + /** + * + * @param context a set of data to extract values from as required for the build pom + * @param lexicalHandlerConsumer the lexical handler consumer + * @param consume {@code true} if this factory is being used for creating the consumer pom, otherwise {@code false} + */ public DefaultBuildPomXMLFilterFactory( TransformerContext context, - Consumer lexicalHandlerConsumer ) + Consumer lexicalHandlerConsumer, + boolean consume ) { - super( lexicalHandlerConsumer ); + super( lexicalHandlerConsumer, consume ); this.context = context; } - - public final TransformerContext getContext() - { - return context; - } - + @Override protected Function> getRelativePathMapper() { return p -> Optional.ofNullable( context.getRawModel( p ) ).map( m -> toRelativeProject( m ) ); } - + @Override protected BiFunction getDependencyKeyToVersionMapper() { @@ -66,6 +69,24 @@ protected BiFunction getDependencyKeyToVersionMapper() .orElse( null ); } + @Override + protected Optional getChangelist() + { + return Optional.ofNullable( context.getUserProperty( "changelist" ) ); + } + + @Override + protected Optional getRevision() + { + return Optional.ofNullable( context.getUserProperty( "revision" ) ); + } + + @Override + protected Optional getSha1() + { + return Optional.ofNullable( context.getUserProperty( "sha1" ) ); + } + private static RelativeProject toRelativeProject( final Model m ) { String groupId = m.getGroupId(); @@ -82,7 +103,7 @@ private static RelativeProject toRelativeProject( final Model m ) return new RelativeProject( groupId, m.getArtifactId(), version ); } - + private static String toVersion( final Model m ) { String version = m.getVersion(); diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java index 8b31908f5d13..e12e56654b34 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java @@ -25,16 +25,21 @@ import org.apache.maven.artifact.versioning.ArtifactVersion; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.inject.Named; @@ -69,6 +74,7 @@ import org.apache.maven.model.inheritance.InheritanceAssembler; import org.apache.maven.model.interpolation.ModelInterpolator; import org.apache.maven.model.io.ModelParseException; +import org.apache.maven.model.io.ModelReader; import org.apache.maven.model.management.DependencyManagementInjector; import org.apache.maven.model.management.PluginManagementInjector; import org.apache.maven.model.merge.ModelMerger; @@ -117,7 +123,7 @@ public class DefaultModelBuilder @Inject private ModelUrlNormalizer modelUrlNormalizer; - + @Inject private SuperPomProvider superPomProvider; @@ -151,7 +157,7 @@ public class DefaultModelBuilder @Inject private ReportingConverter reportingConverter; - + private ModelMerger modelMerger = new FileToRawModelMerger(); public DefaultModelBuilder setModelProcessor( ModelProcessor modelProcessor ) @@ -255,7 +261,13 @@ public DefaultModelBuilder setReportingConverter( ReportingConverter reportingCo this.reportingConverter = reportingConverter; return this; } - + + @Override + public DefaultTransformerContextBuilder newTransformerContextBuilder() + { + return new DefaultTransformerContextBuilder(); + } + @Override public ModelBuildingResult build( ModelBuildingRequest request ) throws ModelBuildingException @@ -272,6 +284,33 @@ protected ModelBuildingResult build( ModelBuildingRequest request, Collection activePomProfiles = profileSelector.getActiveProfiles( inputModel.getProfiles(), + profileActivationContext, problems ); + + // model normalization + problems.setSource( inputModel ); + modelNormalizer.mergeDuplicates( inputModel, request, problems ); + + Map interpolatedActivations = getProfileActivations( inputModel, false ); + injectProfileActivations( inputModel, interpolatedActivations ); + + // profile injection + for ( Profile activeProfile : activePomProfiles ) + { + profileInjector.injectProfile( inputModel, activeProfile, request, problems ); + } + + for ( Profile activeProfile : activeExternalProfiles ) { - inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems ); + profileInjector.injectProfile( inputModel, activeProfile, request, problems ); } + } + + @SuppressWarnings( "checkstyle:methodlength" ) + private Model readEffectiveModel( final ModelBuildingRequest request, final DefaultModelBuildingResult result, + DefaultModelProblemCollector problems ) + throws ModelBuildingException + { + Model inputModel = + readRawModel( request, problems ); problems.setRootModel( inputModel ); ModelData resultData = new ModelData( request.getModelSource(), inputModel ); ModelData superData = new ModelData( null, getSuperModel() ); + // profile activation + DefaultProfileActivationContext profileActivationContext = getProfileActivationContext( request ); + + List activeExternalProfiles = result.getActiveExternalProfiles(); + + if ( !activeExternalProfiles.isEmpty() ) + { + Properties profileProps = new Properties(); + for ( Profile profile : activeExternalProfiles ) + { + profileProps.putAll( profile.getProperties() ); + } + profileProps.putAll( profileActivationContext.getUserProperties() ); + profileActivationContext.setUserProperties( profileProps ); + } + Collection parentIds = new LinkedHashSet<>(); - List lineage = new ArrayList<>(); + + List lineage = new ArrayList<>(); for ( ModelData currentData = resultData; currentData != null; ) { - lineage.add( currentData ); + String modelId = currentData.getId(); + result.addModelId( modelId ); Model rawModel = currentData.getModel(); - currentData.setRawModel( rawModel ); + result.setRawModel( modelId, rawModel ); + + profileActivationContext.setProjectProperties( rawModel.getProperties() ); + problems.setSource( rawModel ); + List activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(), + profileActivationContext, problems ); + result.setActivePomProfiles( modelId, activePomProfiles ); Model tmpModel = rawModel.clone(); - currentData.setModel( tmpModel ); problems.setSource( tmpModel ); // model normalization modelNormalizer.mergeDuplicates( tmpModel, request, problems ); - profileActivationContext.setProjectProperties( tmpModel.getProperties() ); - - List activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(), - profileActivationContext, problems ); - currentData.setActiveProfiles( activePomProfiles ); - - Map interpolatedActivations = getProfileActivations( rawModel, false ); + Map interpolatedActivations = getProfileActivations( tmpModel, false ); injectProfileActivations( tmpModel, interpolatedActivations ); // profile injection - for ( Profile activeProfile : activePomProfiles ) + for ( Profile activeProfile : result.getActivePomProfiles( modelId ) ) { profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); } @@ -343,8 +424,11 @@ protected ModelBuildingResult build( ModelBuildingRequest request, Collection " ); + message.append( parentId ).append( " -> " ); } message.append( parentData.getId() ); @@ -396,86 +463,50 @@ else if ( !parentIds.add( parentData.getId() ) ) } } - problems.setSource( inputModel ); + problems.setSource( result.getRawModel() ); checkPluginVersions( lineage, request, problems ); // inheritance assembly assembleInheritance( lineage, request, problems ); - Model resultModel = resultData.getModel(); + Model resultModel = lineage.get( 0 ); + + // consider caching inherited model problems.setSource( resultModel ); problems.setRootModel( resultModel ); // model interpolation resultModel = interpolateModel( resultModel, request, problems ); - resultData.setModel( resultModel ); - - if ( resultModel.getParent() != null ) - { - final ModelData parentData = lineage.get( 1 ); - if ( parentData.getVersion() == null || parentData.getVersion().contains( "${" ) ) - { - final Model interpolatedParent = interpolateModel( parentData.getModel(), request, problems ); - // parentData.setModel( interpolatedParent ); - parentData.setVersion( interpolatedParent.getVersion() ); - } - } // url normalization modelUrlNormalizer.normalize( resultModel, request ); - // Now the fully interpolated model is available: reconfigure the resolver - configureResolver( request.getModelResolver(), resultModel, problems, true ); - - resultData.setGroupId( resultModel.getGroupId() ); - resultData.setArtifactId( resultModel.getArtifactId() ); - resultData.setVersion( resultModel.getVersion() ); - - if ( request.getPomFile() != null ) - { - intoCache( request.getModelCache(), new FileModelSource( request.getPomFile() ), ModelCacheTag.RAW, - resultData ); - } - else - { - intoCache( request.getModelCache(), request.getModelSource(), ModelCacheTag.RAW, resultData ); - } - result.setEffectiveModel( resultModel ); - - for ( ModelData currentData : lineage ) - { - String modelId = ( currentData != superData ) ? currentData.getId() : ""; - - result.addModelId( modelId ); - result.setActivePomProfiles( modelId, currentData.getActiveProfiles() ); - result.setRawModel( modelId, currentData.getRawModel() ); - } - if ( !request.isTwoPhaseBuilding() ) - { - build( request, result, importIds ); - } + // Now the fully interpolated model is available: reconfigure the resolver + configureResolver( request.getModelResolver(), resultModel, problems, true ); - return result; + return resultModel; } @Override - public ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result ) + public ModelBuildingResult build( final ModelBuildingRequest request, final ModelBuildingResult result ) throws ModelBuildingException { return build( request, result, new LinkedHashSet<>() ); } - private ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result, + private ModelBuildingResult build( final ModelBuildingRequest request, final ModelBuildingResult phaseOneResult, Collection imports ) throws ModelBuildingException { - // phase 2 - Model resultModel = result.getEffectiveModel(); + DefaultModelBuildingResult result = asDefaultModelBuildingResult( phaseOneResult ); DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result ); + + // phase 2 + Model resultModel = readEffectiveModel( request, result, problems ); problems.setSource( resultModel ); problems.setRootModel( resultModel ); @@ -529,6 +560,18 @@ private ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingRe return result; } + private DefaultModelBuildingResult asDefaultModelBuildingResult( ModelBuildingResult phaseOneResult ) + { + if ( phaseOneResult instanceof DefaultModelBuildingResult ) + { + return (DefaultModelBuildingResult) phaseOneResult; + } + else + { + return new DefaultModelBuildingResult( phaseOneResult ); + } + } + @Override public Result buildRawModel( File pomFile, int validationLevel, boolean locationTracking ) { @@ -538,7 +581,7 @@ public Result buildRawModel( File pomFile, int validationLevel, new DefaultModelProblemCollector( new DefaultModelBuildingResult() ); try { - return newResult( readModel( null, pomFile, request, collector ), collector.getProblems() ); + return newResult( readFileModel( request, collector ), collector.getProblems() ); } catch ( ModelBuildingException e ) { @@ -547,24 +590,15 @@ public Result buildRawModel( File pomFile, int validationLevel, } @SuppressWarnings( "checkstyle:methodlength" ) - private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingRequest request, - DefaultModelProblemCollector problems ) + private Model readFileModel( ModelBuildingRequest request, + DefaultModelProblemCollector problems ) throws ModelBuildingException { - if ( modelSource == null ) + ModelSource modelSource = request.getModelSource(); + Model model = getModelFromCache( modelSource, request.getModelCache() ); + if ( model != null ) { - modelSource = - new FileModelSource( Objects.requireNonNull( pomFile, "neither pomFile nor modelSource can be null" ) ); - } - - Model model; - if ( pomFile == null ) - { - model = getModelFromCache( modelSource, request.getModelCache() ); - if ( model != null ) - { - return model; - } + return model; } problems.setSource( modelSource.getLocation() ); @@ -577,7 +611,7 @@ private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingReq options.put( ModelProcessor.SOURCE, modelSource ); InputSource source; - if ( request.isLocationTracking() ) + if ( request.isLocationTracking() ) { source = (InputSource) options.computeIfAbsent( ModelProcessor.INPUT_SOURCE, k -> new InputSource() ); } @@ -609,7 +643,7 @@ private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingReq throw e; } - if ( pomFile != null ) + if ( modelSource instanceof FileModelSource ) { problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.V20 ) .setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() ) @@ -655,69 +689,106 @@ private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingReq .setMessage( "Non-readable POM " + modelSource.getLocation() + ": " + msg ).setException( e ) ); throw problems.newModelBuildingException(); } - if ( pomFile != null ) - { - model.setPomFile( pomFile ); - } - else if ( modelSource instanceof FileModelSource ) + + if ( modelSource instanceof FileModelSource ) { model.setPomFile( ( (FileModelSource) modelSource ).getFile() ); } problems.setSource( model ); modelValidator.validateFileModel( model, request, problems ); - request.setFileModel( model ); - - if ( Features.buildConsumer().isActive() && pomFile != null ) + + if ( hasFatalErrors( problems ) ) + { + throw problems.newModelBuildingException(); + } + + intoCache( request.getModelCache(), modelSource, ModelCacheTag.FILE, model ); + if ( modelSource instanceof FileModelSource ) { + if ( request.getTransformerContextBuilder() instanceof DefaultTransformerContextBuilder ) + { + DefaultTransformerContextBuilder contextBuilder = + (DefaultTransformerContextBuilder) request.getTransformerContextBuilder(); + contextBuilder.putSource( getGroupId( model ), model.getArtifactId(), modelSource ); + } + } + + return model; + } + + private Model readRawModel( ModelBuildingRequest request, DefaultModelProblemCollector problems ) + throws ModelBuildingException + { + ModelSource modelSource = request.getModelSource(); + + ModelData cachedData = fromCache( request.getModelCache(), modelSource, ModelCacheTag.RAW ); + if ( cachedData != null ) + { + return cachedData.getModel(); + } + + Model rawModel; + if ( Features.buildConsumer().isActive() && modelSource instanceof FileModelSource ) + { + rawModel = readFileModel( request, problems ); + File pomFile = ( (FileModelSource) modelSource ).getFile(); + + TransformerContext context = null; + if ( request.getTransformerContextBuilder() != null ) + { + context = request.getTransformerContextBuilder().initialize( request, problems ); + } + try { - Model rawModel = - modelProcessor.read( pomFile, - Collections.singletonMap( "transformerContext", request.getTransformerContext() ) ); - - model.setPomFile( pomFile ); - - // model with locationTrackers, required for proper feedback during validations - model = request.getFileModel().clone(); - + // must implement TransformContext, but should use request to access system properties/modelcache + Model transformedFileModel = modelProcessor.read( pomFile, + Collections.singletonMap( ModelReader.TRANSFORMER_CONTEXT, context ) ); + + // rawModel with locationTrackers, required for proper feedback during validations + // Apply enriched data - modelMerger.merge( model, rawModel, false, null ); + modelMerger.merge( rawModel, transformedFileModel, false, null ); } catch ( IOException e ) { problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V37 ).setException( e ) ); } } + else if ( request.getFileModel() == null ) + { + rawModel = readFileModel( request, problems ); + } + else + { + rawModel = request.getFileModel().clone(); + } - modelValidator.validateRawModel( model, request, problems ); + modelValidator.validateRawModel( rawModel, request, problems ); if ( hasFatalErrors( problems ) ) { throw problems.newModelBuildingException(); } - if ( pomFile != null ) - { - intoCache( request.getModelCache(), modelSource, ModelCacheTag.FILEMODEL, model ); - } + String groupId = getGroupId( rawModel ); + String artifactId = rawModel.getArtifactId(); + String version = getVersion( rawModel ); - String groupId = getGroupId( model ); - String artifactId = model.getArtifactId(); - String version = getVersion( model ); - - ModelData modelData = new ModelData( modelSource, model, groupId, artifactId, version ); + ModelData modelData = new ModelData( modelSource, rawModel, groupId, artifactId, version ); intoCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, modelData ); + intoCache( request.getModelCache(), modelSource, ModelCacheTag.RAW, modelData ); - return model; + return rawModel; } - private Model getModelFromCache( ModelSource modelSource, ModelCache cache ) + private Model getModelFromCache( Source source, ModelCache cache ) { Model model; - if ( modelSource instanceof ArtifactModelSource ) + if ( source instanceof ArtifactModelSource ) { - ArtifactModelSource artifactModelSource = ( ArtifactModelSource ) modelSource; + ArtifactModelSource artifactModelSource = ( ArtifactModelSource ) source; ModelData modelData = fromCache( cache, artifactModelSource.getGroupId(), artifactModelSource.getArtifactId(), artifactModelSource.getVersion(), ModelCacheTag.RAW ); @@ -725,19 +796,14 @@ private Model getModelFromCache( ModelSource modelSource, ModelCache cache ) { model = modelData.getModel(); } - else + else { model = null; } } else { - model = fromCache( cache, modelSource, ModelCacheTag.FILEMODEL ); - - if ( model != null ) - { - model = model.clone(); - } + model = fromCache( cache, source, ModelCacheTag.FILE ); } return model; } @@ -752,13 +818,13 @@ private String getGroupId( Model model ) return groupId; } - private String getVersion( Model model ) + private String getVersion( Model model ) { String version = model.getVersion(); if ( version == null && model.getParent() != null ) { version = model.getParent().getVersion(); - } + } return version; } @@ -807,7 +873,7 @@ private void configureResolver( ModelResolver modelResolver, Model model, Defaul } } - private void checkPluginVersions( List lineage, ModelBuildingRequest request, + private void checkPluginVersions( List lineage, ModelBuildingRequest request, ModelProblemCollector problems ) { if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) @@ -821,7 +887,7 @@ private void checkPluginVersions( List lineage, ModelBuildingRequest for ( int i = lineage.size() - 1; i >= 0; i-- ) { - Model model = lineage.get( i ).getModel(); + Model model = lineage.get( i ); Build build = model.getBuild(); if ( build != null ) { @@ -859,13 +925,13 @@ private void checkPluginVersions( List lineage, ModelBuildingRequest } } - private void assembleInheritance( List lineage, ModelBuildingRequest request, + private void assembleInheritance( List lineage, ModelBuildingRequest request, ModelProblemCollector problems ) { for ( int i = lineage.size() - 2; i >= 0; i-- ) { - Model parent = lineage.get( i + 1 ).getModel(); - Model child = lineage.get( i ).getModel(); + Model parent = lineage.get( i + 1 ); + Model child = lineage.get( i ); inheritanceAssembler.assembleModelInheritance( child, parent, request, problems ); } } @@ -940,7 +1006,7 @@ private Model interpolateModel( Model model, ModelBuildingRequest request, Model problems.add( mpcr ); } - + } interpolatedModel.setPomFile( model.getPomFile() ); @@ -950,8 +1016,8 @@ private Model interpolateModel( Model model, ModelBuildingRequest request, Model return interpolatedModel; } - private ModelData readParent( Model childModel, ModelSource childSource, ModelBuildingRequest request, - DefaultModelProblemCollector problems ) + private ModelData readParent( Model childModel, Source childSource, ModelBuildingRequest request, + ModelBuildingResult result, DefaultModelProblemCollector problems ) throws ModelBuildingException { ModelData parentData = null; @@ -960,18 +1026,18 @@ private ModelData readParent( Model childModel, ModelSource childSource, ModelBu if ( parent != null ) { - ModelSource expectedParentSource = getParentPomFile( childModel, childSource ); + Source expectedParentSource = getParentPomFile( childModel, childSource ); if ( expectedParentSource != null ) { - ModelData candidateData = readParentLocally( childModel, childSource, request, problems ); + ModelData candidateData = readParentLocally( childModel, childSource, request, result, problems ); if ( candidateData != null ) { /* - * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, + * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, * the child's GAV should match with that parent, too. If it doesn't, we ignore the cache and - * resolve externally, to mimic the behavior if the cache didn't exist in the first place. + * resolve externally, to mimic the behavior if the cache didn't exist in the first place. * Otherwise, the cache would obscure a bad POM. */ try @@ -995,11 +1061,11 @@ private ModelData readParent( Model childModel, ModelSource childSource, ModelBu if ( parentData == null ) { - ModelData candidateData = fromCache( request.getModelCache(), + ModelData candidateData = fromCache( request.getModelCache(), parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), ModelCacheTag.RAW ); - + if ( candidateData != null && candidateData.getSource() instanceof ArtifactModelSource ) { // ArtifactModelSource means repositorySource @@ -1007,15 +1073,15 @@ private ModelData readParent( Model childModel, ModelSource childSource, ModelBu } else { - parentData = readParentExternally( childModel, request, problems ); - - intoCache( request.getModelCache(), + parentData = readParentExternally( childModel, request, result, problems ); + + intoCache( request.getModelCache(), parentData.getGroupId(), parentData.getArtifactId(), parentData.getVersion(), ModelCacheTag.RAW, parentData ); } } - - if ( parentData != null ) + + if ( parentData != null ) { Model parentModel = parentData.getModel(); @@ -1032,8 +1098,8 @@ private ModelData readParent( Model childModel, ModelSource childSource, ModelBu return parentData; } - private ModelData readParentLocally( Model childModel, ModelSource childSource, ModelBuildingRequest request, - DefaultModelProblemCollector problems ) + private ModelData readParentLocally( Model childModel, Source childSource, ModelBuildingRequest request, + ModelBuildingResult result, DefaultModelProblemCollector problems ) throws ModelBuildingException { final Parent parent = childModel.getParent(); @@ -1049,7 +1115,16 @@ private ModelData readParentLocally( Model childModel, ModelSource childSource, return null; } - candidateModel = readModel( candidateSource, null, request, problems ); + ModelBuildingRequest candidateBuildRequest = new FilterModelBuildingRequest( request ) + { + @Override + public ModelSource getModelSource() + { + return candidateSource; + } + }; + + candidateModel = readRawModel( candidateBuildRequest, problems ); } else { @@ -1083,11 +1158,6 @@ private ModelData readParentLocally( Model childModel, ModelSource childSource, groupId = candidateModel.getParent().getGroupId(); } String artifactId = candidateModel.getArtifactId(); - String version = candidateModel.getVersion(); - if ( version == null && candidateModel.getParent() != null ) - { - version = candidateModel.getParent().getVersion(); - } if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null || !artifactId.equals( parent.getArtifactId() ) ) @@ -1107,6 +1177,12 @@ private ModelData readParentLocally( Model childModel, ModelSource childSource, .setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ) ); return null; } + + String version = candidateModel.getVersion(); + if ( version == null && candidateModel.getParent() != null ) + { + version = candidateModel.getParent().getVersion(); + } if ( version != null && parent.getVersion() != null && !version.equals( parent.getVersion() ) ) { try @@ -1161,12 +1237,10 @@ private ModelData readParentLocally( Model childModel, ModelSource childSource, * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; } */ - ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version ); - - return parentData; + return new ModelData( candidateSource, candidateModel, groupId, artifactId, version ); } - private ModelSource getParentPomFile( Model childModel, ModelSource source ) + private ModelSource getParentPomFile( Model childModel, Source source ) { if ( !( source instanceof ModelSource2 ) ) { @@ -1184,12 +1258,12 @@ private ModelSource getParentPomFile( Model childModel, ModelSource source ) } private ModelData readParentExternally( Model childModel, ModelBuildingRequest request, - DefaultModelProblemCollector problems ) + ModelBuildingResult result, DefaultModelProblemCollector problems ) throws ModelBuildingException { problems.setSource( childModel ); - Parent parent = childModel.getParent().clone(); + Parent parent = childModel.getParent(); String groupId = parent.getGroupId(); String artifactId = parent.getArtifactId(); @@ -1237,20 +1311,28 @@ private ModelData readParentExternally( Model childModel, ModelBuildingRequest r throw problems.newModelBuildingException(); } - ModelBuildingRequest lenientRequest = request; - if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) + int validationLevel = Math.min( request.getValidationLevel(), ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ); + ModelBuildingRequest lenientRequest = new FilterModelBuildingRequest( request ) { - lenientRequest = new FilterModelBuildingRequest( request ) + @Override + public int getValidationLevel() { - @Override - public int getValidationLevel() - { - return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0; - } - }; - } + return validationLevel; + } + + @Override + public ModelSource getModelSource() + { + return modelSource; + } + @Override + public Model getFileModel() + { + return null; + } + }; - Model parentModel = readModel( modelSource, null, lenientRequest, problems ); + Model parentModel = readRawModel( lenientRequest, problems ); if ( !parent.getVersion().equals( version ) ) { @@ -1276,10 +1358,8 @@ public int getValidationLevel() // MNG-2199: What else to check here ? } - ModelData parentData = new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(), - parent.getVersion() ); - - return parentData; + return new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(), + parent.getVersion() ); } private Model getSuperModel() @@ -1486,7 +1566,7 @@ private void intoCache( ModelCache modelCache, Source source, ModelCacheTag< } } - private T fromCache( ModelCache modelCache, String groupId, String artifactId, String version, + private static T fromCache( ModelCache modelCache, String groupId, String artifactId, String version, ModelCacheTag tag ) { if ( modelCache != null ) @@ -1500,7 +1580,7 @@ private T fromCache( ModelCache modelCache, String groupId, String artifactI return null; } - private T fromCache( ModelCache modelCache, Source source, ModelCacheTag tag ) + private static T fromCache( ModelCache modelCache, Source source, ModelCacheTag tag ) { if ( modelCache != null ) { @@ -1564,8 +1644,8 @@ protected boolean hasFatalErrors( ModelProblemCollectorExt problems ) /** * As long as Maven controls the BuildPomXMLFilter, the entities that need merging are known. - * All others can simply be copied from source to target to restore the locationTracker - * + * All others can simply be copied from source to target to restore the locationTracker + * * @author Robert Scholte * @since 4.0.0 */ @@ -1577,7 +1657,7 @@ protected void mergeBuild_Extensions( Build target, Build source, boolean source { // don't merge } - + @Override protected void mergeBuildBase_Resources( BuildBase target, BuildBase source, boolean sourceDominant, @@ -1585,21 +1665,21 @@ protected void mergeBuildBase_Resources( BuildBase target, BuildBase source, boo { // don't merge } - + @Override protected void mergeBuildBase_TestResources( BuildBase target, BuildBase source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergeCiManagement_Notifiers( CiManagement target, CiManagement source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergeDependencyManagement_Dependencies( DependencyManagement target, DependencyManagement source, boolean sourceDominant, Map context ) @@ -1608,14 +1688,14 @@ protected void mergeDependencyManagement_Dependencies( DependencyManagement targ target.getDependencies().stream().forEach( t -> mergeDependency( t, sourceIterator.next(), sourceDominant, context ) ); } - + @Override protected void mergeDependency_Exclusions( Dependency target, Dependency source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergeModel_Contributors( Model target, Model source, boolean sourceDominant, Map context ) @@ -1629,21 +1709,21 @@ protected void mergeModel_Developers( Model target, Model source, boolean source { // don't merge } - + @Override protected void mergeModel_Licenses( Model target, Model source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergeModel_MailingLists( Model target, Model source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergeModel_Profiles( Model target, Model source, boolean sourceDominant, Map context ) @@ -1652,7 +1732,7 @@ protected void mergeModel_Profiles( Model target, Model source, boolean sourceDo target.getProfiles().stream().forEach( t -> mergeProfile( t, sourceIterator.next(), sourceDominant, context ) ); } - + @Override protected void mergeModelBase_Dependencies( ModelBase target, ModelBase source, boolean sourceDominant, Map context ) @@ -1661,21 +1741,21 @@ protected void mergeModelBase_Dependencies( ModelBase target, ModelBase source, target.getDependencies().stream().forEach( t -> mergeDependency( t, sourceIterator.next(), sourceDominant, context ) ); } - + @Override protected void mergeModelBase_PluginRepositories( ModelBase target, ModelBase source, boolean sourceDominant, Map context ) { target.setPluginRepositories( source.getPluginRepositories() ); } - + @Override protected void mergeModelBase_Repositories( ModelBase target, ModelBase source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergePlugin_Dependencies( Plugin target, Plugin source, boolean sourceDominant, Map context ) @@ -1684,14 +1764,14 @@ protected void mergePlugin_Dependencies( Plugin target, Plugin source, boolean s target.getDependencies().stream().forEach( t -> mergeDependency( t, sourceIterator.next(), sourceDominant, context ) ); } - + @Override protected void mergePlugin_Executions( Plugin target, Plugin source, boolean sourceDominant, Map context ) { // don't merge } - + @Override protected void mergeReporting_Plugins( Reporting target, Reporting source, boolean sourceDominant, Map context ) @@ -1705,7 +1785,7 @@ protected void mergeReportPlugin_ReportSets( ReportPlugin target, ReportPlugin s { // don't merge } - + @Override protected void mergePluginContainer_Plugins( PluginContainer target, PluginContainer source, boolean sourceDominant, Map context ) @@ -1713,4 +1793,132 @@ protected void mergePluginContainer_Plugins( PluginContainer target, PluginConta // don't merge } } + + /** + * Builds up the transformer context. + * After the buildplan is ready, the build()-method returns the immutable context useful during distribution. + * This is an inner class, as it must be able to call readRawModel() + * + * @author Robert Scholte + * @since 4.0.0 + */ + private class DefaultTransformerContextBuilder implements TransformerContextBuilder + { + private final DefaultTransformerContext context = new DefaultTransformerContext(); + + private final Map> mappedSources + = new ConcurrentHashMap<>( 64 ); + + /** + * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt + * + * @param request + * @param collector + * @return + */ + @Override + public TransformerContext initialize( ModelBuildingRequest request, ModelProblemCollector collector ) + { + // We must assume the TransformerContext was created using this.newTransformerContextBuilder() + DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector; + return new TransformerContext() + { + @Override + public String getUserProperty( String key ) + { + return context.userProperties.computeIfAbsent( key, + k -> request.getUserProperties().getProperty( key ) ); + } + + @Override + public Model getRawModel( String gId, String aId ) + { + return context.modelByGA.computeIfAbsent( new DefaultTransformerContext.GAKey( gId, aId ), + k -> findRawModel( gId, aId ) ); + } + + @Override + public Model getRawModel( Path path ) + { + return context.modelByPath.computeIfAbsent( path, k -> findRawModel( path ) ); + } + + private Model findRawModel( String groupId, String artifactId ) + { + Source source = getSource( groupId, artifactId ); + if ( source != null ) + { + try + { + ModelBuildingRequest gaBuildingRequest = new FilterModelBuildingRequest( request ) + { + @Override + public ModelSource getModelSource() + { + return (ModelSource) source; + } + + }; + return readRawModel( gaBuildingRequest, problems ); + } + catch ( ModelBuildingException e ) + { + // gathered with problem collector + } + } + return null; + } + + private Model findRawModel( Path p ) + { + if ( !Files.isRegularFile( p ) ) + { + throw new IllegalArgumentException( "Not a regular file: " + p ); + } + + DefaultModelBuildingRequest req = new DefaultModelBuildingRequest( request ) + .setPomFile( p.toFile() ) + .setModelSource( new FileModelSource( p.toFile() ) ); + + try + { + return readRawModel( req, problems ); + } + catch ( ModelBuildingException e ) + { + // gathered with problem collector + } + return null; + } + }; + } + + @Override + public TransformerContext build() + { + return context; + } + + public Source getSource( String groupId, String artifactId ) + { + Set sources = mappedSources.get( new DefaultTransformerContext.GAKey( groupId, artifactId ) ); + if ( sources == null ) + { + return null; + } + return sources.stream().reduce( ( a, b ) -> + { + throw new IllegalStateException( String.format( "No unique Source for %s:%s: %s and %s", + groupId, artifactId, + a.getLocation(), b.getLocation() ) ); + } ).orElse( null ); + } + + public void putSource( String groupId, String artifactId, Source source ) + { + mappedSources.computeIfAbsent( new DefaultTransformerContext.GAKey( groupId, artifactId ), + k -> new HashSet<>() ).add( source ); + } + + } } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingRequest.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingRequest.java index 2012bb1a578f..44ad4250b873 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingRequest.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingRequest.java @@ -40,8 +40,6 @@ public class DefaultModelBuildingRequest { private Model fileModel; - private Model rawModel; - private File pomFile; private ModelSource modelSource; @@ -74,7 +72,7 @@ public class DefaultModelBuildingRequest private WorkspaceModelResolver workspaceResolver; - private TransformerContext context; + private TransformerContextBuilder contextBuilder; /** * Creates an empty request. @@ -104,6 +102,7 @@ public DefaultModelBuildingRequest( ModelBuildingRequest request ) setModelResolver( request.getModelResolver() ); setModelBuildingListener( request.getModelBuildingListener() ); setModelCache( request.getModelCache() ); + setTransformerContextBuilder( request.getTransformerContextBuilder() ); } @Override @@ -397,17 +396,16 @@ public ModelBuildingRequest setFileModel( Model fileModel ) this.fileModel = fileModel; return this; } - + @Override public Model getRawModel() { - return rawModel; + return null; } @Override public ModelBuildingRequest setRawModel( Model rawModel ) { - this.rawModel = rawModel; return this; } @@ -423,17 +421,18 @@ public ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver wo this.workspaceResolver = workspaceResolver; return this; } - + @Override - public TransformerContext getTransformerContext() + public TransformerContextBuilder getTransformerContextBuilder() { - return context; + return contextBuilder; } @Override - public ModelBuildingRequest setTransformerContext( TransformerContext context ) + public ModelBuildingRequest setTransformerContextBuilder( TransformerContextBuilder contextBuilder ) { - this.context = context; + this.contextBuilder = contextBuilder; return this; } + } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingResult.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingResult.java index 13b771436078..b2fcfa05f02d 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingResult.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuildingResult.java @@ -36,6 +36,7 @@ class DefaultModelBuildingResult implements ModelBuildingResult { + private Model fileModel; private Model effectiveModel; @@ -58,6 +59,35 @@ class DefaultModelBuildingResult problems = new ArrayList<>(); } + DefaultModelBuildingResult( ModelBuildingResult result ) + { + this(); + this.activeExternalProfiles.addAll( result.getActiveExternalProfiles() ); + this.effectiveModel = result.getEffectiveModel(); + this.fileModel = result.getFileModel(); + this.problems.addAll( result.getProblems() ); + + for ( String modelId : result.getModelIds() ) + { + this.modelIds.add( modelId ); + this.rawModels.put( modelId, result.getRawModel( modelId ) ); + this.activePomProfiles.put( modelId, result.getActivePomProfiles( modelId ) ); + } + } + + @Override + public Model getFileModel() + { + return fileModel; + } + + public DefaultModelBuildingResult setFileModel( Model fileModel ) + { + this.fileModel = fileModel; + + return this; + } + @Override public Model getEffectiveModel() { diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java new file mode 100644 index 000000000000..080c62bf9a8f --- /dev/null +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java @@ -0,0 +1,95 @@ +package org.apache.maven.model.building; + +/* + * 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.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.apache.maven.model.Model; + +/** + * + * @author Robert Scholte + * @since 4.0.0 + */ +class DefaultTransformerContext implements TransformerContext +{ + final Map userProperties = new HashMap<>(); + + final Map modelByPath = new HashMap<>(); + + final Map modelByGA = new HashMap<>(); + + @Override + public String getUserProperty( String key ) + { + return userProperties.get( key ); + } + + @Override + public Model getRawModel( Path p ) + { + return modelByPath.get( p ); + } + + @Override + public Model getRawModel( String groupId, String artifactId ) + { + return modelByGA.get( new GAKey( groupId, artifactId ) ); + } + + static class GAKey + { + private final String groupId; + private final String artifactId; + private final int hashCode; + + GAKey( String groupId, String artifactId ) + { + this.groupId = groupId; + this.artifactId = artifactId; + this.hashCode = Objects.hash( groupId, artifactId ); + } + + @Override + public int hashCode() + { + return hashCode; + } + + @Override + public boolean equals( Object obj ) + { + if ( this == obj ) + { + return true; + } + if ( !( obj instanceof GAKey ) ) + { + return false; + } + + GAKey other = (GAKey) obj; + return Objects.equals( artifactId, other.artifactId ) && Objects.equals( groupId, other.groupId ); + } + } +} diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/FilterModelBuildingRequest.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/FilterModelBuildingRequest.java index 1dd2643e1dac..1374cbb23958 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/FilterModelBuildingRequest.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/FilterModelBuildingRequest.java @@ -268,7 +268,7 @@ public ModelBuildingRequest setFileModel( Model fileModel ) request.setFileModel( fileModel ); return this; } - + @Override public Model getRawModel() { @@ -294,17 +294,17 @@ public ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver wo request.setWorkspaceModelResolver( workspaceResolver ); return this; } - + @Override - public TransformerContext getTransformerContext() + public TransformerContextBuilder getTransformerContextBuilder() { - return request.getTransformerContext(); + return request.getTransformerContextBuilder(); } @Override - public ModelBuildingRequest setTransformerContext( TransformerContext context ) + public ModelBuildingRequest setTransformerContextBuilder( TransformerContextBuilder contextBuilder ) { - request.setTransformerContext( context ); + request.setTransformerContextBuilder( contextBuilder ); return this; } } \ No newline at end of file diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java index e42469acf089..74c1fa6c311b 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuilder.java @@ -60,4 +60,6 @@ ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult res */ Result buildRawModel( File pomFile, int validationLevel, boolean locationTracking ); + TransformerContextBuilder newTransformerContextBuilder(); + } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java index c9451efdce7e..bbd9e8b3feac 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingRequest.java @@ -63,16 +63,19 @@ public interface ModelBuildingRequest * Denotes strict validation as recommended by the current Maven version. */ int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_3_0; - + /** - * - * @return the file model + * Gets the file model to build (with profile activation). + * If not set, model source will be used to load file model. + * + * @return The file model to build or {@code null} if not set. * @since 4.0.0 */ Model getFileModel(); - + /** - * + * Set the file model with profile activation + * * @param fileModel * @return This request, never {@code null}. * @since 4.0.0 @@ -80,17 +83,15 @@ public interface ModelBuildingRequest ModelBuildingRequest setFileModel( Model fileModel ); /** - * Gets the raw model to build. If not set, model source will be used to load raw model. - * - * @return The raw model to build or {@code null} if not set. + * @deprecated rawModel is never set, instead the fileModel is set */ + @Deprecated Model getRawModel(); /** - * Set raw model. - * - * @param rawModel + * @deprecated setting the rawModel has no effect, instead the fileModel of phase one will be set */ + @Deprecated ModelBuildingRequest setRawModel( Model rawModel ); /** @@ -349,10 +350,9 @@ public interface ModelBuildingRequest WorkspaceModelResolver getWorkspaceModelResolver(); ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver workspaceResolver ); - - TransformerContext getTransformerContext(); - ModelBuildingRequest setTransformerContext( TransformerContext context ); - - + TransformerContextBuilder getTransformerContextBuilder(); + + ModelBuildingRequest setTransformerContextBuilder( TransformerContextBuilder contextBuilder ); + } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingResult.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingResult.java index 44b12958ee9b..603d2140ad9b 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingResult.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingResult.java @@ -42,6 +42,13 @@ public interface ModelBuildingResult */ List getModelIds(); + /** + * + * @return the file model + * @since 4.0.0 + */ + Model getFileModel(); + /** * Gets the assembled model. * diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheTag.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheTag.java index f38bb627137a..7e0cb0daa441 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheTag.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCacheTag.java @@ -65,7 +65,7 @@ interface ModelCacheTag T fromCache( T data ); /** - * The tag used to denote raw model data. + * The tag used for the raw model without profile activation */ ModelCacheTag RAW = new ModelCacheTag() { @@ -129,12 +129,16 @@ public DependencyManagement fromCache( DependencyManagement data ) }; - ModelCacheTag FILEMODEL = new ModelCacheTag() + /** + * The tag used for the file model without profile activation + * @since 4.0.0 + */ + ModelCacheTag FILE = new ModelCacheTag() { @Override public String getName() { - return "file-model"; + return "file"; } @Override diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelData.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelData.java index 1f39ad443b80..1ef4341a8e9c 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelData.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelData.java @@ -19,10 +19,10 @@ * under the License. */ -import java.util.List; +import java.util.Objects; +import org.apache.maven.building.Source; import org.apache.maven.model.Model; -import org.apache.maven.model.Profile; /** * Holds a model along with some auxiliary information. This internal utility class assists the model builder during POM @@ -32,13 +32,9 @@ */ class ModelData { - private final ModelSource source; + private final Source source; - private Model model; - - private Model rawModel; - - private List activeProfiles; + private final Model model; private String groupId; @@ -51,7 +47,7 @@ class ModelData * * @param model The model to wrap, may be {@code null}. */ - ModelData( ModelSource source, Model model ) + ModelData( Source source, Model model ) { this.source = source; this.model = model; @@ -65,16 +61,16 @@ class ModelData * @param artifactId The effective artifact identifier of the model, may be {@code null}. * @param version The effective version of the model, may be {@code null}. */ - ModelData( ModelSource source, Model model, String groupId, String artifactId, String version ) + ModelData( Source source, Model model, String groupId, String artifactId, String version ) { this.source = source; this.model = model; - setGroupId( groupId ); - setArtifactId( artifactId ); - setVersion( version ); + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; } - public ModelSource getSource() + public Source getSource() { return source; } @@ -89,56 +85,6 @@ public Model getModel() return model; } - /** - * Sets the model being wrapped. - * - * @param model The model, may be {@code null}. - */ - public void setModel( Model model ) - { - this.model = model; - } - - /** - * Gets the raw model being wrapped. - * - * @return The raw model or {@code null} if not set. - */ - public Model getRawModel() - { - return rawModel; - } - - /** - * Sets the raw model being wrapped. - * - * @param rawModel The raw model, may be {@code null}. - */ - public void setRawModel( Model rawModel ) - { - this.rawModel = rawModel; - } - - /** - * Gets the active profiles from the model. - * - * @return The active profiles or {@code null} if not set. - */ - public List getActiveProfiles() - { - return activeProfiles; - } - - /** - * Sets the active profiles from the model. - * - * @param activeProfiles The active profiles, may be {@code null}. - */ - public void setActiveProfiles( List activeProfiles ) - { - this.activeProfiles = activeProfiles; - } - /** * Gets the effective group identifier of the model. * @@ -149,16 +95,6 @@ public String getGroupId() return ( groupId != null ) ? groupId : ""; } - /** - * Sets the effective group identifier of the model. - * - * @param groupId The effective group identifier of the model, may be {@code null}. - */ - public void setGroupId( String groupId ) - { - this.groupId = groupId; - } - /** * Gets the effective artifact identifier of the model. * @@ -169,16 +105,6 @@ public String getArtifactId() return ( artifactId != null ) ? artifactId : ""; } - /** - * Sets the effective artifact identifier of the model. - * - * @param artifactId The effective artifact identifier of the model, may be {@code null}. - */ - public void setArtifactId( String artifactId ) - { - this.artifactId = artifactId; - } - /** * Gets the effective version of the model. * @@ -190,27 +116,14 @@ public String getVersion() } /** - * Sets the effective version of the model. - * - * @param version The effective version of the model, may be {@code null}. - */ - public void setVersion( String version ) - { - this.version = version; - } - - /** - * Gets the effective identifier of the model in the form {@code ::}. + * Gets unique identifier of the model * * @return The effective identifier of the model, never {@code null}. */ public String getId() { - StringBuilder buffer = new StringBuilder( 128 ); - - buffer.append( getGroupId() ).append( ':' ).append( getArtifactId() ).append( ':' ).append( getVersion() ); - - return buffer.toString(); + // if source is null, it is the supermodel, which can be accessed via empty string + return Objects.toString( source, "" ); } @Override diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java index b502e1e738c0..a2556ceaa271 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelSourceTransformer.java @@ -24,12 +24,24 @@ import java.nio.file.Path; /** - * + * The ModelSourceTransformer is a way to transform the local pom while streaming the input. + * + * The {@link #transform(Path, TransformerContext)} method uses a Path on purpose, to ensure the + * local pom is the the original source. + * * @author Robert Scholte * @since 4.0.0 */ public interface ModelSourceTransformer { + /** + * + * @param pomFile the pom file, cannot be null + * @param context the context, cannot be null + * @return the InputStream for the ModelReader + * @throws IOException if an I/O error occurs + * @throws TransformerException if the transformation fails + */ InputStream transform( Path pomFile, TransformerContext context ) throws IOException, TransformerException; } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java index 3779a3901480..d7a43dcc6c36 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContext.java @@ -47,10 +47,10 @@ public interface TransformerContext /** * Get the model based on the path, will be used to resolve the parent based on relativePath * - * @param p the path + * @param pomFile the path to the pomFile * @return the model, otherwise {@code null} */ - Model getRawModel( Path p ); + Model getRawModel( Path pomFile ); /** * Get the model from the reactor based on the groupId and artifactId, will be used for reactor dependencies @@ -60,5 +60,5 @@ public interface TransformerContext * @return the model, otherwise {@code null} * @throws IllegalStateException if multiple versions of the same GA are part of the reactor */ - Model getRawModel( String groupId, String artifactId ) throws IllegalStateException; + Model getRawModel( String groupId, String artifactId ); } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContextBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContextBuilder.java new file mode 100644 index 000000000000..6117f2a3f512 --- /dev/null +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/TransformerContextBuilder.java @@ -0,0 +1,46 @@ +package org.apache.maven.model.building; + +/* + * 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. + */ + +/** + * The transformerContextBuilder is responsible for initializing the TransformerContext. + * In case rawModels are missing, it could do new buildingRequests on the ModelBuilder. + * + * @author Robert Scholte + * @since 4.0.0 + */ +public interface TransformerContextBuilder +{ + /** + * This method is used to initialize the TransformerContext + * + * @param request the modelBuildingRequest + * @param problems the problemCollector + * @return the mutable transformerContext + */ + TransformerContext initialize( ModelBuildingRequest request, ModelProblemCollector problems ); + + /** + * The immutable transformerContext, can be used after the buildplan is finished. + * + * @return the immutable transformerContext + */ + TransformerContext build(); +} diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java b/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java index 1bae7475e8f2..1d8b2646ad59 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/io/DefaultModelReader.java @@ -59,18 +59,14 @@ public void setTransformer( ModelSourceTransformer transformer ) { this.transformer = transformer; } - + @Override public Model read( File input, Map options ) throws IOException { Objects.requireNonNull( input, "input cannot be null" ); - TransformerContext context = null; - if ( options != null ) - { - context = (TransformerContext) options.get( "transformerContext" ); - } + TransformerContext context = getTransformerContext( options ); final InputStream is; if ( context == null ) @@ -135,6 +131,12 @@ private InputSource getSource( Map options ) return (InputSource) value; } + private TransformerContext getTransformerContext( Map options ) + { + Object value = ( options != null ) ? options.get( TRANSFORMER_CONTEXT ) : null; + return (TransformerContext) value; + } + private Model read( Reader reader, boolean strict, InputSource source ) throws IOException { diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java b/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java index 75a5ebeea385..04b1a3017cdb 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/io/ModelReader.java @@ -48,6 +48,12 @@ public interface ModelReader */ String INPUT_SOURCE = "org.apache.maven.model.io.inputSource"; + /** + * The key for the option to provide a transformer context, which can be used to transform the input while reading + * to get an advanced version of the model. + */ + String TRANSFORMER_CONTEXT = "transformerContext"; + /** * Reads the model from the specified file. * diff --git a/maven-model-builder/src/site/apt/index.apt b/maven-model-builder/src/site/apt/index.apt index e18ad9deb331..2f2085214bbb 100644 --- a/maven-model-builder/src/site/apt/index.apt +++ b/maven-model-builder/src/site/apt/index.apt @@ -43,6 +43,22 @@ Maven Model Builder Notice that model interpolation hasn't happened yet, then interpolation for file-based activation is limited to <<<$\{basedir}>>> (since Maven 3), System properties and request properties + ** file model validation: <<>> ({{{./apidocs/org/apache/maven/model/validation/ModelValidator.html}javadoc}}), + with its <<>> implementation + ({{{./xref/org/apache/maven/model/validation/DefaultModelValidator.html}source}}) + + [] + + * phase 2, with optional plugin processing + + ** Build up a raw model by re-reading the file and enrich it based on information available in the reactor. Some features: + + *** Resolve version of versionless parents based on realtivePath (including ci-friendly versions) + + *** Resolve version of versionless dependencies that are part of the reactor + + [] + ** raw model validation: <<>> ({{{./apidocs/org/apache/maven/model/validation/ModelValidator.html}javadoc}}), with its <<>> implementation ({{{./xref/org/apache/maven/model/validation/DefaultModelValidator.html}source}}) @@ -65,10 +81,6 @@ Maven Model Builder with its <<>> implementation ({{{./xref/org/apache/maven/model/path/DefaultUrlNormalizer.html}source}}) - [] - - * phase 2, with optional plugin processing - ** model path translation: <<>> ({{{./apidocs/org/apache/maven/model/path/ModelPathTranslator.html}javadoc}}), with its <<>> implementation ({{{./xref/org/apache/maven/model/path/DefaultModelPathTranslator.html}source}}) @@ -124,7 +136,7 @@ Maven Model Builder Notice that the 5 URLs from the model (<<>>, <<>>, <<>>, <<>> and <<>>) have a special inheritance handling: - + ** if not configured in current model, the inherited value is the parent's one with current artifact id appended, ** since Maven 3.5.0, if <<>> POM property value is defined, it is used instead of artifact id: @@ -194,6 +206,11 @@ Maven Model Builder *----+------+------+ | <<>> | Local user settings (see {{{../maven-settings/settings.html}settings reference}}) | <<<$\{settings.localRepository\}>>> | *----+------+------+ +| <<>> \ +<<>> \ +<<>> | CI friendly placeholders for the project version (see {{{/maven-ci-friendly.html}Maven CI Friendly Versions}}) | <<<1.0.0-$\{changelist\}-SNAPSHOT>>> | +*----+------+------+ + ** Notice diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java index d2a9e600db4d..252b1d95f906 100644 --- a/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java +++ b/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java @@ -69,8 +69,6 @@ private SimpleProblemCollector validateEffective( String pom, int level ) SimpleProblemCollector problems = new SimpleProblemCollector( model ); - request.setFileModel( model ); - validator.validateEffectiveModel( model, request, problems ); return problems; @@ -87,8 +85,6 @@ private SimpleProblemCollector validateRaw( String pom, int level ) validator.validateFileModel( model, request, problems ); - request.setFileModel( model ); - validator.validateRawModel( model, request, problems ); return problems; diff --git a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java index 066be354cf9e..2a194d11d137 100644 --- a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java +++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/BuildPomXMLFilterFactory.java @@ -41,16 +41,19 @@ */ public class BuildPomXMLFilterFactory { + private final boolean consume; + private final Consumer lexicalHandlerConsumer; - - public BuildPomXMLFilterFactory() + + public BuildPomXMLFilterFactory( Consumer lexicalHandlerConsumer ) { - this( null ); + this( lexicalHandlerConsumer, false ); } - - public BuildPomXMLFilterFactory( Consumer lexicalHandlerConsumer ) + + public BuildPomXMLFilterFactory( Consumer lexicalHandlerConsumer, boolean consume ) { this.lexicalHandlerConsumer = lexicalHandlerConsumer; + this.consume = consume; } /** @@ -89,6 +92,18 @@ public final BuildPomXMLFilter get( Path projectFile ) parent = parentFilter; } + CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter( consume ); + getChangelist().ifPresent( ciFriendlyFilter::setChangelist ); + getRevision().ifPresent( ciFriendlyFilter::setRevision ); + getSha1().ifPresent( ciFriendlyFilter::setSha1 ); + + if ( ciFriendlyFilter.isSet() ) + { + ciFriendlyFilter.setParent( parent ); + parent.setLexicalHandler( ciFriendlyFilter ); + parent = ciFriendlyFilter; + } + return new BuildPomXMLFilter( parent ); } @@ -98,7 +113,7 @@ private XMLReader getXMLReader() throws SAXException, ParserConfigurationExcepti xmlReader.setFeature( "http://xml.org/sax/features/namespaces", true ); return xmlReader; } - + /** * @return the mapper or {@code null} if relativePaths don't need to be mapped */ @@ -111,4 +126,22 @@ protected BiFunction getDependencyKeyToVersionMapper() { return null; } + + // getters for the 3 magic properties of CIFriendly versions ( https://maven.apache.org/maven-ci-friendly.html ) + + protected Optional getChangelist() + { + return Optional.empty(); + } + + protected Optional getRevision() + { + return Optional.empty(); + } + + protected Optional getSha1() + { + return Optional.empty(); + } + } diff --git a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java index 0bda110379a7..2da8ddc3c420 100644 --- a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java +++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilter.java @@ -26,27 +26,30 @@ /** * Resolves all ci-friendly properties occurrences between version-tags - * + * * @author Robert Scholte * @since 4.0.0 */ class CiFriendlyXMLFilter extends AbstractSAXFilter { + private final boolean replace; + private Function replaceChain = Function.identity(); - - private String characters; - + + private String characters; + private boolean parseVersion; - - CiFriendlyXMLFilter() + + CiFriendlyXMLFilter( boolean replace ) { - super(); + this.replace = replace; } - CiFriendlyXMLFilter( AbstractSAXFilter parent ) + CiFriendlyXMLFilter( AbstractSAXFilter parent, boolean replace ) { super( parent ); + this.replace = replace; } public CiFriendlyXMLFilter setChangelist( String changelist ) @@ -54,7 +57,7 @@ public CiFriendlyXMLFilter setChangelist( String changelist ) replaceChain = replaceChain.andThen( t -> t.replace( "${changelist}", changelist ) ); return this; } - + public CiFriendlyXMLFilter setRevision( String revision ) { replaceChain = replaceChain.andThen( t -> t.replace( "${revision}", revision ) ); @@ -66,7 +69,7 @@ public CiFriendlyXMLFilter setSha1( String sha1 ) replaceChain = replaceChain.andThen( t -> t.replace( "${sha1}", sha1 ) ); return this; } - + /** * @return {@code true} is any of the ci properties is set, otherwise {@code false} */ @@ -74,7 +77,7 @@ public boolean isSet() { return !replaceChain.equals( Function.identity() ); } - + @Override public void characters( char[] ch, int start, int length ) throws SAXException @@ -100,7 +103,7 @@ public void startElement( String uri, String localName, String qName, Attributes super.startElement( uri, localName, qName, atts ); } - + @Override public void endElement( String uri, String localName, String qName ) throws SAXException @@ -108,7 +111,7 @@ public void endElement( String uri, String localName, String qName ) if ( parseVersion ) { // assuming this has the best performance - if ( characters != null && characters.contains( "${" ) ) + if ( replace && characters != null && characters.contains( "${" ) ) { char[] ch = replaceChain.apply( characters ).toCharArray(); super.characters( ch, 0, ch.length ); @@ -124,4 +127,4 @@ public void endElement( String uri, String localName, String qName ) super.endElement( uri, localName, qName ); } -} \ No newline at end of file +} diff --git a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java index 18e32080f447..0f2bd7e3c05b 100644 --- a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java +++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterFactory.java @@ -20,7 +20,6 @@ */ import java.nio.file.Path; -import java.util.Optional; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerConfigurationException; @@ -46,20 +45,10 @@ public final ConsumerPomXMLFilter get( Path projectPath ) { BuildPomXMLFilter parent = buildPomXMLFilterFactory.get( projectPath ); - + // Ensure that xs:any elements aren't touched by next filters AbstractSAXFilter filter = new FastForwardFilter( parent ); - - CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter( filter ); - getChangelist().ifPresent( ciFriendlyFilter::setChangelist ); - getRevision().ifPresent( ciFriendlyFilter::setRevision ); - getSha1().ifPresent( ciFriendlyFilter::setSha1 ); - - if ( ciFriendlyFilter.isSet() ) - { - filter = ciFriendlyFilter; - } - + // Strip modules filter = new ModulesXMLFilter( filter ); // Adjust relativePath @@ -67,22 +56,4 @@ public final ConsumerPomXMLFilter get( Path projectPath ) return new ConsumerPomXMLFilter( filter ); } - - // getters for the 3 magic properties of CIFriendly versions ( https://maven.apache.org/maven-ci-friendly.html ) - - protected Optional getChangelist() - { - return Optional.empty(); - } - - protected Optional getRevision() - { - return Optional.empty(); - } - - protected Optional getSha1() - { - return Optional.empty(); - } - } diff --git a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java index ac4818882714..2d717f0d02a5 100644 --- a/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java +++ b/maven-xml/src/main/java/org/apache/maven/xml/sax/filter/ParentXMLFilter.java @@ -19,6 +19,7 @@ * under the License. */ +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -34,10 +35,10 @@ *

* Transforms relativePath to version. * We could decide to simply allow {@code }, but let's require the GA for now for checking - * This filter does NOT remove the relativePath (which is done by {@link RelativePathXMLFilter}, it will only - * optionally include the version based on the path + * This filter does NOT remove the relativePath (which is done by {@link RelativePathXMLFilter}, it will only + * optionally include the version based on the path *

- * + * * @author Robert Scholte * @since 4.0.0 */ @@ -51,24 +52,24 @@ class ParentXMLFilter // whiteSpace after , to be used to position private String parentWhitespace = ""; - + private String groupId; private String artifactId; - + private String relativePath; private boolean hasVersion; - + + private boolean hasRelativePath; + private Optional resolvedParent; private final Function> relativePathMapper; - + private Path projectPath; /** - * - * * @param relativePathMapper */ ParentXMLFilter( Function> relativePathMapper ) @@ -80,7 +81,7 @@ public void setProjectPath( Path projectPath ) { this.projectPath = projectPath; } - + @Override protected boolean isParsing() { @@ -92,7 +93,7 @@ protected String getState() { return state; } - + @Override public void startElement( String uri, final String localName, String qName, Attributes atts ) throws SAXException @@ -105,10 +106,13 @@ public void startElement( String uri, final String localName, String qName, Attr if ( parsingParent ) { state = localName; - + hasVersion |= "version".equals( localName ); + + // can be set to empty on purpose to enforce repository download + hasRelativePath |= "relativePath".equals( localName ); } - + super.startElement( uri, localName, qName, atts ); } @@ -119,9 +123,9 @@ public void characters( char[] ch, int start, int length ) if ( parsingParent ) { final String eventState = state; - + final String charSegment = new String( ch, start, length ); - + switch ( eventState ) { case "parent": @@ -140,7 +144,7 @@ public void characters( char[] ch, int start, int length ) break; } } - + super.characters( ch, start, length ); } @@ -153,33 +157,37 @@ public void endElement( String uri, final String localName, String qName ) switch ( localName ) { case "parent": - if ( !hasVersion || relativePath != null ) + if ( !hasVersion && ( !hasRelativePath || relativePath != null ) ) { resolvedParent = resolveRelativePath( Paths.get( Objects.toString( relativePath, "../pom.xml" ) ) ); } - + else + { + resolvedParent = Optional.empty(); + } + if ( !hasVersion && resolvedParent.isPresent() ) { try ( Includer i = super.include() ) { super.characters( parentWhitespace.toCharArray(), 0, parentWhitespace.length() ); - + String versionQName = SAXEventUtils.renameQName( qName, "version" ); - + super.startElement( uri, "version", versionQName, null ); - + String resolvedParentVersion = resolvedParent.get().getVersion(); - + super.characters( resolvedParentVersion.toCharArray(), 0, resolvedParentVersion.length() ); - + super.endElement( uri, "version", versionQName ); } } super.executeEvents(); - + parsingParent = false; break; default: @@ -187,20 +195,25 @@ public void endElement( String uri, final String localName, String qName ) break; } } - + super.endElement( uri, localName, qName ); state = ""; } protected Optional resolveRelativePath( Path relativePath ) { - Optional mappedProject = - relativePathMapper.apply( projectPath.resolve( relativePath ).normalize() ); - + Path pomPath = projectPath.resolve( relativePath ); + if ( Files.isDirectory( pomPath ) ) + { + pomPath = pomPath.resolve( "pom.xml" ); + } + + Optional mappedProject = relativePathMapper.apply( pomPath.normalize() ); + if ( mappedProject.isPresent() ) { RelativeProject project = mappedProject.get(); - + if ( Objects.equals( groupId, project.getGroupId() ) && Objects.equals( artifactId, project.getArtifactId() ) ) { diff --git a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilterTest.java index e93c7201527b..7dcdc4f5d8fa 100644 --- a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilterTest.java +++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/CiFriendlyXMLFilterTest.java @@ -35,7 +35,7 @@ public class CiFriendlyXMLFilterTest extends AbstractXMLFilterTests @Before public void setUp() { - filter = new CiFriendlyXMLFilter(); + filter = new CiFriendlyXMLFilter( true ); filter.setChangelist( "CHANGELIST" ); } diff --git a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java index 70dc5b4f7ae6..5ea73a4846db 100644 --- a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java +++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ConsumerPomXMLFilterTest.java @@ -47,7 +47,7 @@ protected String omitXmlDeclaration() protected AbstractSAXFilter getFilter( Consumer lexicalHandlerConsumer ) throws SAXException, ParserConfigurationException, TransformerConfigurationException { - final BuildPomXMLFilterFactory buildPomXMLFilterFactory = new BuildPomXMLFilterFactory( lexicalHandlerConsumer ) + final BuildPomXMLFilterFactory buildPomXMLFilterFactory = new BuildPomXMLFilterFactory( lexicalHandlerConsumer, true ) { @Override protected Function> getRelativePathMapper() @@ -60,10 +60,7 @@ protected BiFunction getDependencyKeyToVersionMapper() { return null; } - }; - - ConsumerPomXMLFilter filter = new ConsumerPomXMLFilterFactory( buildPomXMLFilterFactory ) - { + @Override protected Optional getSha1() { @@ -81,7 +78,11 @@ protected Optional getChangelist() { return Optional.of( "CL" ); } - }.get( Paths.get( "pom.xml" ) ); + + }; + + ConsumerPomXMLFilter filter = + new ConsumerPomXMLFilterFactory( buildPomXMLFilterFactory ).get( Paths.get( "pom.xml" ) ); filter.setFeature( "http://xml.org/sax/features/namespaces", true ); return filter; } @@ -234,7 +235,7 @@ public void licenseHeader() throws Exception { String actual = transform( input ); assertThat( actual ).and( expected ).areIdentical(); } - + @Test public void lexicalHandler() throws Exception { @@ -245,7 +246,7 @@ public void lexicalHandler() throws Exception + "" + "" + ""; - String expected = "\n" + + String expected = "\n" + ""; String actual = transform( input ); assertThat( actual ).and( expected ).areIdentical(); diff --git a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java index eee035946e9f..809aef5e0872 100644 --- a/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java +++ b/maven-xml/src/test/java/org/apache/maven/xml/sax/filter/ParentXMLFilterTest.java @@ -38,15 +38,15 @@ public class ParentXMLFilterTest extends AbstractXMLFilterTests protected ParentXMLFilter getFilter( Consumer lexicalHandlerConsumer ) throws TransformerException, SAXException, ParserConfigurationException { - ParentXMLFilter filter = new ParentXMLFilter( x -> Optional.of( new RelativeProject( "GROUPID", + ParentXMLFilter filter = new ParentXMLFilter( x -> Optional.of( new RelativeProject( "GROUPID", "ARTIFACTID", "1.0.0" ) ) ); filter.setProjectPath( Paths.get( "pom.xml").toAbsolutePath() ); lexicalHandlerConsumer.accept( filter ); - + return filter; } - + @Test public void testMinimum() throws Exception { @@ -89,6 +89,31 @@ public void testDefaultRelativePath() throws Exception assertEquals( expected, actual ); } + /** + * An empty relative path means it must downloaded from a repository. + * That implies that the version cannot be solved (if missing, Maven should complain) + * + * @throws Exception + */ + @Test + public void testEmptyRelativePathNoVersion() throws Exception + { + String input = "" + + "GROUPID" + + "ARTIFACTID" + + "" + + ""; + String expected = "" + + "GROUPID" + + "ARTIFACTID" + + "" // SAX optimization, however "" != null ... + + ""; + + String actual = transform( input ); + + assertEquals( expected, actual ); + } + @Test public void testNoVersion() throws Exception { @@ -114,7 +139,7 @@ public void testInvalidRelativePath() throws Exception { ParentXMLFilter filter = new ParentXMLFilter( x -> Optional.ofNullable( null ) ); filter.setProjectPath( Paths.get( "pom.xml").toAbsolutePath() ); - + String input = "" + "GROUPID" + "ARTIFACTID" @@ -167,7 +192,7 @@ public void testWithWeirdNamespace() throws Exception assertEquals( expected, actual ); } - + @Test public void comment() throws Exception { @@ -189,7 +214,7 @@ public void comment() throws Exception assertEquals( expected, actual ); } - + @Test public void testIndent() throws Exception {