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 d81571da348..4d5d2636532 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 624f6ade9d9..a86cb1fcb6e 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; @@ -386,36 +381,13 @@ 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(); + Model model = result.getFileModel().clone(); - poolBuilder.put( model.getPomFile().toPath(), result.getRawModel() ); + poolBuilder.put( model.getPomFile().toPath(), model ); - 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 ) ); - } - - projectIndex.put( result.getModelIds().get( 0 ), project ); - 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 ) ) @@ -608,6 +571,8 @@ private boolean build( List results, List noErrors = false; } } + + 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 ) { // @@ -1095,16 +1061,22 @@ class InternalConfig private final ReactorModelPool modelPool; private final ReactorModelCache modelCache; + + private final TransformerContextBuilder transformerContextBuilder; - InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, ReactorModelCache modelCache ) + 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 72c25c79f88..20e57b9eca6 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 aa68680279c..b27a87b3fef 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 9766b8d76a5..11196234eed 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 cf095e06387..122b14428ab 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,7 +32,8 @@ import org.xml.sax.ext.LexicalHandler; /** - * + * A BuildPomXMLFilterFactory which is context aware + * * @author Robert Scholte * @since 4.0.0 */ @@ -40,18 +41,20 @@ 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() { @@ -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(); 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 8b31908f5d1..f833c25dd21 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; @@ -256,6 +262,12 @@ public DefaultModelBuilder setReportingConverter( ReportingConverter reportingCo 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 ) { - inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems ); + profileInjector.injectProfile( inputModel, activeProfile, request, problems ); } + for ( Profile activeProfile : activeExternalProfiles ) + { + 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 ); + + result.setEffectiveModel( resultModel ); // 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 ); - } - - 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() ); @@ -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 ( 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(); + } - if ( Features.buildConsumer().isActive() && pomFile != null ) + 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() ) ); + // must implement TransformContext, but should use request to access system properties/modelcache + Model transformedFileModel = modelProcessor.read( pomFile, + Collections.singletonMap( ModelReader.TRANSFORMER_CONTEXT, context ) ); - model.setPomFile( pomFile ); - - // model with locationTrackers, required for proper feedback during validations - model = request.getFileModel().clone(); + // 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( model ); - String artifactId = model.getArtifactId(); - String version = getVersion( model ); + String groupId = getGroupId( rawModel ); + String artifactId = rawModel.getArtifactId(); + String version = getVersion( rawModel ); - 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 ); - - return model; + intoCache( request.getModelCache(), modelSource, ModelCacheTag.RAW, modelData ); + + 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 ); @@ -732,12 +803,7 @@ private Model getModelFromCache( ModelSource modelSource, ModelCache cache ) } else { - model = fromCache( cache, modelSource, ModelCacheTag.FILEMODEL ); - - if ( model != null ) - { - model = model.clone(); - } + model = fromCache( cache, source, ModelCacheTag.FILE ); } return model; } @@ -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 ); } } @@ -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,11 +1026,11 @@ 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 ) { @@ -1007,7 +1073,7 @@ private ModelData readParent( Model childModel, ModelSource childSource, ModelBu } else { - parentData = readParentExternally( childModel, request, problems ); + parentData = readParentExternally( childModel, request, result, problems ); intoCache( request.getModelCache(), parentData.getGroupId(), parentData.getArtifactId(), @@ -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(); @@ -1048,8 +1114,17 @@ private ModelData readParentLocally( Model childModel, ModelSource childSource, { return null; } + + ModelBuildingRequest candidateBuildRequest = new FilterModelBuildingRequest( request ) + { + @Override + public ModelSource getModelSource() + { + return candidateSource; + } + }; - candidateModel = readModel( candidateSource, null, request, problems ); + 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 ) { @@ -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 2012bb1a578..44ad4250b87 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 13b77143607..731c583aec8 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; @@ -57,6 +58,35 @@ class DefaultModelBuildingResult activeExternalProfiles = new ArrayList<>(); 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 00000000000..1930a485248 --- /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 1dd2643e1da..1374cbb2395 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 e42469acf08..85455f66f14 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 @@ -59,5 +59,7 @@ ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult res * Performs only the part of {@link ModelBuilder#build(ModelBuildingRequest)} that loads the raw model */ 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 c9451efdce7..5bd1ddf9859 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 ); /** @@ -350,9 +351,9 @@ public interface ModelBuildingRequest ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver workspaceResolver ); - TransformerContext getTransformerContext(); + TransformerContextBuilder getTransformerContextBuilder(); - ModelBuildingRequest setTransformerContext( TransformerContext context ); + 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 44b12958ee9..f9615c82542 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 @@ -41,6 +41,13 @@ public interface ModelBuildingResult * @return The model identifiers from the lineage of models, never {@code null}. */ 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 f38bb627137..c6fdc1cc473 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 1f39ad443b8..1ef4341a8e9 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 b502e1e738c..bc3743b8a05 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 3779a390148..d7a43dcc6c3 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 00000000000..a10798eb6ad --- /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 1bae7475e8f..23cab356c21 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 @@ -66,11 +66,7 @@ public Model read( File input, Map options ) { 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 ) @@ -134,6 +130,12 @@ private InputSource getSource( Map options ) Object value = ( options != null ) ? options.get( INPUT_SOURCE ) : null; 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 75a5ebeea38..dd6e1e50c31 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 @@ -47,6 +47,12 @@ public interface ModelReader * location tracking. */ 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 e18ad9deb33..3b5355ae925 100644 --- a/maven-model-builder/src/site/apt/index.apt +++ b/maven-model-builder/src/site/apt/index.apt @@ -38,11 +38,27 @@ Maven Model Builder The sequence is divided into 2 phases: * phase 1 - + ** profile activation: see {{{./apidocs/org/apache/maven/model/profile/activation/package-summary.html}available activators}}. 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}}) @@ -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 d2a9e600db4..252b1d95f90 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 066be354cf9..2a194d11d13 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 0bda110379a..ef74880ecf3 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 @@ -33,20 +33,23 @@ class CiFriendlyXMLFilter extends AbstractSAXFilter { + private final boolean replace; + private Function replaceChain = Function.identity(); 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 ) @@ -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 ); 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 18e32080f44..0f2bd7e3c05 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 ac481888271..220e062cee6 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; @@ -60,6 +61,8 @@ class ParentXMLFilter private boolean hasVersion; + private boolean hasRelativePath; + private Optional resolvedParent; private final Function> relativePathMapper; @@ -107,6 +110,9 @@ public void startElement( String uri, final String localName, String qName, Attr 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 ); @@ -153,11 +159,15 @@ 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() ) { @@ -194,8 +204,13 @@ public void endElement( String uri, final String localName, String qName ) 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() ) { 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 e93c7201527..7dcdc4f5d8f 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 70dc5b4f7ae..5ea73a4846d 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 eee035946e9..5cbf707a19a 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 @@ -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 {