From 97b78d6b07d9360f0a6170c4f0c47886c1b9e8ba Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 13 Sep 2022 17:20:42 +0200 Subject: [PATCH 01/16] Introduce a BuildSession --- .../maven/project/DefaultProjectBuilder.java | 1383 +++++++++-------- 1 file changed, 704 insertions(+), 679 deletions(-) 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 7fe5a23b1314..4cb2c50c7234 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 @@ -19,6 +19,10 @@ * under the License. */ +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + import java.io.File; import java.io.IOException; import java.util.AbstractMap; @@ -34,10 +38,6 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.InvalidArtifactRTException; @@ -56,7 +56,6 @@ 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; @@ -69,6 +68,7 @@ import org.apache.maven.model.building.ModelSource; import org.apache.maven.model.building.StringModelSource; import org.apache.maven.model.building.TransformerContext; +import org.apache.maven.model.building.TransformerContextBuilder; import org.apache.maven.model.resolution.ModelResolver; import org.apache.maven.repository.internal.ArtifactDescriptorUtils; import org.apache.maven.repository.internal.ModelCacheFactory; @@ -133,846 +133,903 @@ public DefaultProjectBuilder( public ProjectBuildingResult build( File pomFile, ProjectBuildingRequest request ) throws ProjectBuildingException { - return build( pomFile, new FileModelSource( pomFile ), - new InternalConfig( request, null, null ) ); + return new BuildSession().build( pomFile, new FileModelSource( pomFile ), request ); } @Override public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request ) throws ProjectBuildingException { - return build( null, modelSource, - new InternalConfig( request, null, null ) ); + return new BuildSession().build( null, modelSource, request ); } - private ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConfig config ) + @Override + public ProjectBuildingResult build( Artifact artifact, ProjectBuildingRequest request ) throws ProjectBuildingException { - ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); - - try - { - ProjectBuildingRequest projectBuildingRequest = config.request; - - MavenProject project = projectBuildingRequest.getProject(); - - List modelProblems = null; - Throwable error = null; - - if ( project == null ) - { - ModelBuildingRequest request = getModelBuildingRequest( config ); - - project = new MavenProject(); - project.setFile( pomFile ); + return build( artifact, false, request ); + } - DefaultModelBuildingListener listener = - new DefaultModelBuildingListener( project, projectBuildingHelper, projectBuildingRequest ); - request.setModelBuildingListener( listener ); + @Override + public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request ) + throws ProjectBuildingException + { + return new BuildSession().build( artifact, allowStubModel, request ); + } - request.setPomFile( pomFile ); - request.setModelSource( modelSource ); - request.setLocationTracking( true ); + @Override + public List build( List pomFiles, boolean recursive, ProjectBuildingRequest request ) + throws ProjectBuildingException + { + return new BuildSession().build( pomFiles, recursive, request ); + } - ModelBuildingResult result; - try - { - result = modelBuilder.build( request ); - } - catch ( ModelBuildingException e ) - { - result = e.getResult(); - if ( result == null || result.getEffectiveModel() == null ) - { - throw new ProjectBuildingException( e.getModelId(), e.getMessage(), pomFile, e ); - } - // validation error, continue project building and delay failing to help IDEs - error = e; - } + static class InterimResult + { - modelProblems = result.getProblems(); + File pomFile; - initProject( project, Collections.emptyMap(), true, - result, new HashMap<>(), projectBuildingRequest ); - } - else if ( projectBuildingRequest.isResolveDependencies() ) - { - projectBuildingHelper.selectProjectRealm( project ); - } + ModelBuildingRequest request; - DependencyResolutionResult resolutionResult = null; + ModelBuildingResult result; - if ( projectBuildingRequest.isResolveDependencies() ) - { - resolutionResult = resolveDependencies( project, config.session ); - } + DefaultModelBuildingListener listener; - ProjectBuildingResult result = new DefaultProjectBuildingResult( project, modelProblems, resolutionResult ); + boolean root; - if ( error != null ) - { - ProjectBuildingException e = new ProjectBuildingException( Arrays.asList( result ) ); - e.initCause( error ); - throw e; - } + List modules = Collections.emptyList(); - return result; - } - finally + InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result, + DefaultModelBuildingListener listener, boolean root ) { - Thread.currentThread().setContextClassLoader( oldContextClassLoader ); + this.pomFile = pomFile; + this.request = request; + this.result = result; + this.listener = listener; + this.root = root; } + } - private DependencyResolutionResult resolveDependencies( MavenProject project, RepositorySystemSession session ) + class BuildSession { - DependencyResolutionResult resolutionResult; - - try - { - DefaultDependencyResolutionRequest resolution = new DefaultDependencyResolutionRequest( project, session ); - resolutionResult = dependencyResolver.resolve( resolution ); - } - catch ( DependencyResolutionException e ) + ProjectBuildingResult build( File pomFile, ModelSource modelSource, ProjectBuildingRequest request ) + throws ProjectBuildingException { - resolutionResult = e.getResult(); + InternalConfig config = new InternalConfig( request, null, null ); + return build( pomFile, modelSource, config ); } - Set artifacts = new LinkedHashSet<>(); - if ( resolutionResult.getDependencyGraph() != null ) + ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConfig config ) + throws ProjectBuildingException { - RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(), - Collections.singletonList( project.getArtifact().getId() ), null ); + ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); - // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not - LocalRepositoryManager lrm = session.getLocalRepositoryManager(); - for ( Artifact artifact : artifacts ) + try { - if ( !artifact.isResolved() ) + ProjectBuildingRequest projectBuildingRequest = config.request; + + MavenProject project = projectBuildingRequest.getProject(); + + List modelProblems = null; + Throwable error = null; + + if ( project == null ) { - String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) ); - artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); - } - } - } - project.setResolvedArtifacts( artifacts ); - project.setArtifacts( artifacts ); + ModelBuildingRequest request = getModelBuildingRequest( config ); - return resolutionResult; - } + project = new MavenProject(); + project.setFile( pomFile ); - private List getProfileIds( List profiles ) - { - return profiles.stream().map( org.apache.maven.model.Profile::getId ).collect( Collectors.toList() ); - } + DefaultModelBuildingListener listener = + new DefaultModelBuildingListener( project, projectBuildingHelper, projectBuildingRequest ); + request.setModelBuildingListener( listener ); - private ModelBuildingRequest getModelBuildingRequest( InternalConfig config ) - { - ProjectBuildingRequest configuration = config.request; - - ModelBuildingRequest request = new DefaultModelBuildingRequest(); - - RequestTrace trace = RequestTrace.newChild( null, configuration ).newChild( request ); - - ModelResolver resolver = - new ProjectModelResolver( config.session, trace, repoSystem, repositoryManager, config.repositories, - configuration.getRepositoryMerging(), config.modelPool ); - - request.setValidationLevel( configuration.getValidationLevel() ); - request.setProcessPlugins( configuration.isProcessPlugins() ); - request.setProfiles( configuration.getProfiles() ); - request.setActiveProfileIds( configuration.getActiveProfileIds() ); - request.setInactiveProfileIds( configuration.getInactiveProfileIds() ); - request.setSystemProperties( configuration.getSystemProperties() ); - request.setUserProperties( configuration.getUserProperties() ); - request.setBuildStartTime( configuration.getBuildStartTime() ); - request.setModelResolver( resolver ); - // this is a hint that we want to build 1 file, so don't cache. See MNG-7063 - if ( config.modelPool != null ) - { - request.setModelCache( modelCacheFactory.createCache( config.session ) ); - } - request.setTransformerContextBuilder( config.transformerContextBuilder ); + request.setPomFile( pomFile ); + request.setModelSource( modelSource ); + request.setLocationTracking( true ); - return request; - } + ModelBuildingResult result; + try + { + result = modelBuilder.build( request ); + } + catch ( ModelBuildingException e ) + { + result = e.getResult(); + if ( result == null || result.getEffectiveModel() == null ) + { + throw new ProjectBuildingException( e.getModelId(), e.getMessage(), pomFile, e ); + } + // validation error, continue project building and delay failing to help IDEs + error = e; + } - @Override - public ProjectBuildingResult build( Artifact artifact, ProjectBuildingRequest request ) - throws ProjectBuildingException - { - return build( artifact, false, request ); - } + modelProblems = result.getProblems(); - @Override - public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request ) - throws ProjectBuildingException - { - org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact ); - pomArtifact = ArtifactDescriptorUtils.toPomArtifact( pomArtifact ); + initProject( project, Collections.emptyMap(), true, + result, projectBuildingRequest ); + } + else if ( projectBuildingRequest.isResolveDependencies() ) + { + projectBuildingHelper.selectProjectRealm( project ); + } - InternalConfig config = - new InternalConfig( request, null, null ); + DependencyResolutionResult resolutionResult = null; - boolean localProject; + if ( projectBuildingRequest.isResolveDependencies() ) + { + resolutionResult = resolveDependencies( project, config.session ); + } - try - { - ArtifactRequest pomRequest = new ArtifactRequest(); - pomRequest.setArtifact( pomArtifact ); - pomRequest.setRepositories( config.repositories ); - ArtifactResult pomResult = repoSystem.resolveArtifact( config.session, pomRequest ); + ProjectBuildingResult result = + new DefaultProjectBuildingResult( project, modelProblems, resolutionResult ); - pomArtifact = pomResult.getArtifact(); - localProject = pomResult.getRepository() instanceof WorkspaceRepository; - } - catch ( org.eclipse.aether.resolution.ArtifactResolutionException e ) - { - if ( e.getResults().get( 0 ).isMissing() && allowStubModel ) + if ( error != null ) + { + ProjectBuildingException e = new ProjectBuildingException( Arrays.asList( result ) ); + e.initCause( error ); + throw e; + } + + return result; + } + finally { - return build( null, createStubModelSource( artifact ), config ); + Thread.currentThread().setContextClassLoader( oldContextClassLoader ); } - throw new ProjectBuildingException( artifact.getId(), - "Error resolving project artifact: " + e.getMessage(), e ); } - File pomFile = pomArtifact.getFile(); - - if ( "pom".equals( artifact.getType() ) ) + ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request ) + throws ProjectBuildingException { - artifact.selectVersion( pomArtifact.getVersion() ); - artifact.setFile( pomFile ); - artifact.setResolved( true ); - } + org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact ); + pomArtifact = ArtifactDescriptorUtils.toPomArtifact( pomArtifact ); - if ( localProject ) - { - return build( pomFile, new FileModelSource( pomFile ), config ); - } - else - { - return build( null, new ArtifactModelSource( pomFile, artifact.getGroupId(), artifact.getArtifactId(), - artifact.getVersion() ), - config ); - } - } + InternalConfig config = + new InternalConfig( request, null, null ); - private ModelSource createStubModelSource( Artifact artifact ) - { - StringBuilder buffer = new StringBuilder( 1024 ); + boolean localProject; - buffer.append( "" ); - buffer.append( "" ); - buffer.append( "4.0.0" ); - buffer.append( "" ).append( artifact.getGroupId() ).append( "" ); - buffer.append( "" ).append( artifact.getArtifactId() ).append( "" ); - buffer.append( "" ).append( artifact.getBaseVersion() ).append( "" ); - buffer.append( "" ).append( artifact.getType() ).append( "" ); - buffer.append( "" ); + try + { + ArtifactRequest pomRequest = new ArtifactRequest(); + pomRequest.setArtifact( pomArtifact ); + pomRequest.setRepositories( config.repositories ); + ArtifactResult pomResult = repoSystem.resolveArtifact( config.session, pomRequest ); - return new StringModelSource( buffer, artifact.getId() ); - } + pomArtifact = pomResult.getArtifact(); + localProject = pomResult.getRepository() instanceof WorkspaceRepository; + } + catch ( org.eclipse.aether.resolution.ArtifactResolutionException e ) + { + if ( e.getResults().get( 0 ).isMissing() && allowStubModel ) + { + return build( null, createStubModelSource( artifact ), config ); + } + throw new ProjectBuildingException( artifact.getId(), + "Error resolving project artifact: " + e.getMessage(), e ); + } - @Override - public List build( List pomFiles, boolean recursive, ProjectBuildingRequest request ) - throws ProjectBuildingException - { - List results = new ArrayList<>(); + File pomFile = pomArtifact.getFile(); - List interimResults = new ArrayList<>(); + if ( "pom".equals( artifact.getType() ) ) + { + artifact.selectVersion( pomArtifact.getVersion() ); + artifact.setFile( pomFile ); + artifact.setResolved( true ); + } - ReactorModelPool.Builder poolBuilder = new ReactorModelPool.Builder(); - final ReactorModelPool modelPool = poolBuilder.build(); + if ( localProject ) + { + return build( pomFile, new FileModelSource( pomFile ), config ); + } + else + { + return build( null, new ArtifactModelSource( pomFile, artifact.getGroupId(), artifact.getArtifactId(), + artifact.getVersion() ), + config ); + } + } - InternalConfig config = - new InternalConfig( request, modelPool, modelBuilder.newTransformerContextBuilder() ); - Map projectIndex = new HashMap<>( 256 ); + List build( List pomFiles, boolean recursive, ProjectBuildingRequest request ) + throws ProjectBuildingException + { + List results = new ArrayList<>(); - // phase 1: get file Models from the reactor. - boolean noErrors = - build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive, - config, poolBuilder ); + List interimResults = new ArrayList<>(); - ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); + ReactorModelPool.Builder poolBuilder = new ReactorModelPool.Builder(); + final ReactorModelPool modelPool = poolBuilder.build(); - try - { - // Phase 2: get effective models from the reactor - noErrors = - build( results, new ArrayList<>(), projectIndex, interimResults, request, - new HashMap<>(), config.session ) && noErrors; - } - finally - { - Thread.currentThread().setContextClassLoader( oldContextClassLoader ); - } + InternalConfig config = + new InternalConfig( request, modelPool, modelBuilder.newTransformerContextBuilder() ); - if ( Features.buildConsumer( request.getUserProperties() ).isActive() ) - { - request.getRepositorySession().getData().set( TransformerContext.KEY, - config.transformerContextBuilder.build() ); - } + Map projectIndex = new HashMap<>( 256 ); - if ( !noErrors ) - { - throw new ProjectBuildingException( results ); - } + // phase 1: get file Models from the reactor. + boolean noErrors = + build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive, + config, poolBuilder ); - return results; - } + ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); - @SuppressWarnings( "checkstyle:parameternumber" ) - private boolean build( List results, List interimResults, - Map projectIndex, List pomFiles, Set aggregatorFiles, - boolean root, boolean recursive, InternalConfig config, - ReactorModelPool.Builder poolBuilder ) - { - boolean noErrors = true; + try + { + // Phase 2: get effective models from the reactor + noErrors = + build( results, new ArrayList<>(), projectIndex, interimResults, request, + config.session ) && noErrors; + } + finally + { + Thread.currentThread().setContextClassLoader( oldContextClassLoader ); + } - for ( File pomFile : pomFiles ) - { - aggregatorFiles.add( pomFile ); + if ( Features.buildConsumer( request.getUserProperties() ).isActive() ) + { + request.getRepositorySession().getData().set( TransformerContext.KEY, + config.transformerContextBuilder.build() ); + } - if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, config, - poolBuilder ) ) + if ( !noErrors ) { - noErrors = false; + throw new ProjectBuildingException( results ); } - aggregatorFiles.remove( pomFile ); + return results; } - return noErrors; - } - - @SuppressWarnings( "checkstyle:parameternumber" ) - private boolean build( List results, List interimResults, - Map projectIndex, File pomFile, Set aggregatorFiles, - boolean isRoot, boolean recursive, InternalConfig config, - ReactorModelPool.Builder poolBuilder ) - { - boolean noErrors = true; + @SuppressWarnings( "checkstyle:parameternumber" ) + private boolean build( List results, List interimResults, + Map projectIndex, List pomFiles, Set aggregatorFiles, + boolean root, boolean recursive, InternalConfig config, + ReactorModelPool.Builder poolBuilder ) + { + boolean noErrors = true; - MavenProject project = new MavenProject(); - project.setFile( pomFile ); + for ( File pomFile : pomFiles ) + { + aggregatorFiles.add( pomFile ); - ModelBuildingRequest request = getModelBuildingRequest( config ) - .setPomFile( pomFile ) - .setTwoPhaseBuilding( true ) - .setLocationTracking( true ); + if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, config, + poolBuilder ) ) + { + noErrors = false; + } - DefaultModelBuildingListener listener = - new DefaultModelBuildingListener( project, projectBuildingHelper, config.request ); - request.setModelBuildingListener( listener ); + aggregatorFiles.remove( pomFile ); + } - ModelBuildingResult result; - try - { - result = modelBuilder.build( request ); + return noErrors; } - catch ( ModelBuildingException e ) - { - result = e.getResult(); - if ( result == null || result.getFileModel() == null ) - { - results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); - return false; - } - // validation error, continue project building and delay failing to help IDEs - // result.getProblems().addAll(e.getProblems()) ? - noErrors = false; - } + @SuppressWarnings( "checkstyle:parameternumber" ) + private boolean build( List results, List interimResults, + Map projectIndex, File pomFile, Set aggregatorFiles, + boolean isRoot, boolean recursive, InternalConfig config, + ReactorModelPool.Builder poolBuilder ) + { + boolean noErrors = true; - Model model = result.getFileModel(); + MavenProject project = new MavenProject(); + project.setFile( pomFile ); - poolBuilder.put( model.getPomFile().toPath(), model ); + ModelBuildingRequest request = getModelBuildingRequest( config ) + .setPomFile( pomFile ) + .setTwoPhaseBuilding( true ) + .setLocationTracking( true ); - InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot ); - interimResults.add( interimResult ); + DefaultModelBuildingListener listener = + new DefaultModelBuildingListener( project, projectBuildingHelper, config.request ); + request.setModelBuildingListener( listener ); - if ( recursive ) - { - File basedir = pomFile.getParentFile(); - List moduleFiles = new ArrayList<>(); - for ( String module : model.getModules() ) + ModelBuildingResult result; + try { - if ( StringUtils.isEmpty( module ) ) + result = modelBuilder.build( request ); + } + catch ( ModelBuildingException e ) + { + result = e.getResult(); + if ( result == null || result.getFileModel() == null ) { - continue; + results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); + + return false; } + // validation error, continue project building and delay failing to help IDEs + // result.getProblems().addAll(e.getProblems()) ? + noErrors = false; + } - module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar ); + Model model = result.getFileModel(); - File moduleFile = new File( basedir, module ); + poolBuilder.put( model.getPomFile().toPath(), model ); - if ( moduleFile.isDirectory() ) - { - moduleFile = modelProcessor.locatePom( moduleFile ); - } + InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot ); + interimResults.add( interimResult ); - if ( !moduleFile.isFile() ) + if ( recursive ) + { + File basedir = pomFile.getParentFile(); + List moduleFiles = new ArrayList<>(); + for ( String module : model.getModules() ) { - ModelProblem problem = - new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile - + " does not exist", ModelProblem.Severity.ERROR, ModelProblem.Version.BASE, - model, -1, -1, null ); - result.getProblems().add( problem ); + if ( StringUtils.isEmpty( module ) ) + { + continue; + } - noErrors = false; + module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar ); - continue; - } + File moduleFile = new File( basedir, module ); - if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) - { - // we don't canonicalize on unix to avoid interfering with symlinks - try + if ( moduleFile.isDirectory() ) { - moduleFile = moduleFile.getCanonicalFile(); + moduleFile = modelProcessor.locatePom( moduleFile ); } - catch ( IOException e ) + + if ( !moduleFile.isFile() ) { - moduleFile = moduleFile.getAbsoluteFile(); + ModelProblem problem = + new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile + + " does not exist", ModelProblem.Severity.ERROR, ModelProblem.Version.BASE, + model, -1, -1, null ); + result.getProblems().add( problem ); + + noErrors = false; + + continue; } - } - else - { - moduleFile = new File( moduleFile.toURI().normalize() ); - } - if ( aggregatorFiles.contains( moduleFile ) ) - { - StringBuilder buffer = new StringBuilder( 256 ); - for ( File aggregatorFile : aggregatorFiles ) + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + // we don't canonicalize on unix to avoid interfering with symlinks + try + { + moduleFile = moduleFile.getCanonicalFile(); + } + catch ( IOException e ) + { + moduleFile = moduleFile.getAbsoluteFile(); + } + } + else { - buffer.append( aggregatorFile ).append( " -> " ); + moduleFile = new File( moduleFile.toURI().normalize() ); } - buffer.append( moduleFile ); - ModelProblem problem = - new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile - + " forms aggregation cycle " + buffer, ModelProblem.Severity.ERROR, - ModelProblem.Version.BASE, model, -1, -1, null ); - result.getProblems().add( problem ); + if ( aggregatorFiles.contains( moduleFile ) ) + { + StringBuilder buffer = new StringBuilder( 256 ); + for ( File aggregatorFile : aggregatorFiles ) + { + buffer.append( aggregatorFile ).append( " -> " ); + } + buffer.append( moduleFile ); + + ModelProblem problem = + new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile + + " forms aggregation cycle " + buffer, ModelProblem.Severity.ERROR, + ModelProblem.Version.BASE, model, -1, -1, null ); + result.getProblems().add( problem ); - noErrors = false; + noErrors = false; + + continue; + } - continue; + moduleFiles.add( moduleFile ); } - moduleFiles.add( moduleFile ); + interimResult.modules = new ArrayList<>(); + + if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false, + recursive, config, poolBuilder ) ) + { + noErrors = false; + } } - interimResult.modules = new ArrayList<>(); + projectIndex.put( pomFile, project ); - if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false, - recursive, config, poolBuilder ) ) - { - noErrors = false; - } + return noErrors; } - projectIndex.put( pomFile, project ); + private boolean build( List results, List projects, + Map projectIndex, List interimResults, + ProjectBuildingRequest request, + RepositorySystemSession session ) + { + boolean noErrors = true; - return noErrors; - } - - static class InterimResult - { - - File pomFile; - - ModelBuildingRequest request; - - ModelBuildingResult result; - - DefaultModelBuildingListener listener; - - boolean root; - - List modules = Collections.emptyList(); - - InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result, - DefaultModelBuildingListener listener, boolean root ) - { - this.pomFile = pomFile; - this.request = request; - this.result = result; - this.listener = listener; - this.root = root; - } - - } - - private boolean build( List results, List projects, - Map projectIndex, List interimResults, - ProjectBuildingRequest request, Map profilesXmls, - RepositorySystemSession session ) - { - boolean noErrors = true; - - for ( InterimResult interimResult : interimResults ) - { - MavenProject project = interimResult.listener.getProject(); - try + for ( InterimResult interimResult : interimResults ) { - ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result ); - - // 2nd pass of initialization: resolve and build parent if necessary + MavenProject project = interimResult.listener.getProject(); try { - initProject( project, projectIndex, true, result, profilesXmls, request ); - } - catch ( InvalidArtifactRTException iarte ) - { - result.getProblems().add( new DefaultModelProblem( null, ModelProblem.Severity.ERROR, null, - result.getEffectiveModel(), -1, -1, iarte ) ); - } + ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result ); - List modules = new ArrayList<>(); - noErrors = - build( results, modules, projectIndex, interimResult.modules, request, profilesXmls, session ) - && noErrors; + // 2nd pass of initialization: resolve and build parent if necessary + try + { + initProject( project, projectIndex, true, result, request ); + } + catch ( InvalidArtifactRTException iarte ) + { + result.getProblems().add( new DefaultModelProblem( null, ModelProblem.Severity.ERROR, null, + result.getEffectiveModel(), -1, -1, iarte ) ); + } - projects.addAll( modules ); - projects.add( project ); + List modules = new ArrayList<>(); + noErrors = build( results, modules, projectIndex, interimResult.modules, request, session ) + && noErrors; - project.setExecutionRoot( interimResult.root ); - project.setCollectedProjects( modules ); - DependencyResolutionResult resolutionResult = null; - if ( request.isResolveDependencies() ) - { - resolutionResult = resolveDependencies( project, session ); - } + projects.addAll( modules ); + projects.add( project ); - results.add( new DefaultProjectBuildingResult( project, result.getProblems(), resolutionResult ) ); - } - catch ( ModelBuildingException e ) - { - DefaultProjectBuildingResult result = null; - if ( project == null || interimResult.result.getEffectiveModel() == null ) - { - result = new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ); + project.setExecutionRoot( interimResult.root ); + project.setCollectedProjects( modules ); + DependencyResolutionResult resolutionResult = null; + if ( request.isResolveDependencies() ) + { + resolutionResult = resolveDependencies( project, session ); + } + + results.add( new DefaultProjectBuildingResult( project, result.getProblems(), resolutionResult ) ); } - else + catch ( ModelBuildingException e ) { - project.setModel( interimResult.result.getEffectiveModel() ); + DefaultProjectBuildingResult result = 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 ); - result = new DefaultProjectBuildingResult( project, e.getProblems(), null ); + noErrors = false; } - results.add( result ); - - noErrors = false; } - } - - return noErrors; - } - - @SuppressWarnings( "checkstyle:methodlength" ) - private void initProject( MavenProject project, Map projects, - boolean buildParentIfNotExisting, ModelBuildingResult result, - Map profilesXmls, ProjectBuildingRequest projectBuildingRequest ) - { - project.setModel( result.getEffectiveModel() ); - project.setOriginalModel( result.getFileModel() ); - initParent( project, projects, buildParentIfNotExisting, result, projectBuildingRequest ); - - Artifact projectArtifact = - repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null, - project.getPackaging() ); - project.setArtifact( projectArtifact ); - - if ( project.getFile() != null && buildParentIfNotExisting ) // only set those on 2nd phase, ignore on 1st pass - { - Build build = project.getBuild(); - project.addScriptSourceRoot( build.getScriptSourceDirectory() ); - project.addCompileSourceRoot( build.getSourceDirectory() ); - project.addTestCompileSourceRoot( build.getTestSourceDirectory() ); + return noErrors; } - List activeProfiles = new ArrayList<>(); - activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) ); - activeProfiles.addAll( result.getActiveExternalProfiles() ); - project.setActiveProfiles( activeProfiles ); - - project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) ); - for ( String modelId : result.getModelIds() ) + @SuppressWarnings( "checkstyle:methodlength" ) + private void initProject( MavenProject project, Map projects, + boolean buildParentIfNotExisting, ModelBuildingResult result, + ProjectBuildingRequest projectBuildingRequest ) { - project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) ); - } + project.setModel( result.getEffectiveModel() ); + project.setOriginalModel( result.getFileModel() ); - // - // All the parts that were taken out of MavenProject for Maven 4.0.0 - // + initParent( project, projects, buildParentIfNotExisting, result, projectBuildingRequest ); - project.setProjectBuildingRequest( projectBuildingRequest ); + Artifact projectArtifact = + repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), + project.getVersion(), null, project.getPackaging() ); + project.setArtifact( projectArtifact ); - // pluginArtifacts - Set pluginArtifacts = new HashSet<>(); - for ( Plugin plugin : project.getBuildPlugins() ) - { - Artifact artifact = repositorySystem.createPluginArtifact( plugin ); - - if ( artifact != null ) + // only set those on 2nd phase, ignore on 1st pass + if ( project.getFile() != null && buildParentIfNotExisting ) { - pluginArtifacts.add( artifact ); + Build build = project.getBuild(); + project.addScriptSourceRoot( build.getScriptSourceDirectory() ); + project.addCompileSourceRoot( build.getSourceDirectory() ); + project.addTestCompileSourceRoot( build.getTestSourceDirectory() ); } - } - project.setPluginArtifacts( pluginArtifacts ); - // reportArtifacts - Set reportArtifacts = new HashSet<>(); - for ( ReportPlugin report : project.getReportPlugins() ) - { - Plugin pp = new Plugin(); - pp.setGroupId( report.getGroupId() ); - pp.setArtifactId( report.getArtifactId() ); - pp.setVersion( report.getVersion() ); + List activeProfiles = new ArrayList<>(); + activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) ); + activeProfiles.addAll( result.getActiveExternalProfiles() ); + project.setActiveProfiles( activeProfiles ); - Artifact artifact = repositorySystem.createPluginArtifact( pp ); - - if ( artifact != null ) + project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) ); + for ( String modelId : result.getModelIds() ) { - reportArtifacts.add( artifact ); + project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) ); } - } - project.setReportArtifacts( reportArtifacts ); - // extensionArtifacts - Set extensionArtifacts = new HashSet<>(); - List extensions = project.getBuildExtensions(); - if ( extensions != null ) - { - for ( Extension ext : extensions ) + // + // All the parts that were taken out of MavenProject for Maven 4.0.0 + // + + project.setProjectBuildingRequest( projectBuildingRequest ); + + // pluginArtifacts + Set pluginArtifacts = new HashSet<>(); + for ( Plugin plugin : project.getBuildPlugins() ) { - String version; - if ( StringUtils.isEmpty( ext.getVersion() ) ) - { - version = "RELEASE"; - } - else + Artifact artifact = repositorySystem.createPluginArtifact( plugin ); + + if ( artifact != null ) { - version = ext.getVersion(); + pluginArtifacts.add( artifact ); } + } + project.setPluginArtifacts( pluginArtifacts ); + + // reportArtifacts + Set reportArtifacts = new HashSet<>(); + for ( ReportPlugin report : project.getReportPlugins() ) + { + Plugin pp = new Plugin(); + pp.setGroupId( report.getGroupId() ); + pp.setArtifactId( report.getArtifactId() ); + pp.setVersion( report.getVersion() ); - Artifact artifact = - repositorySystem.createArtifact( ext.getGroupId(), ext.getArtifactId(), version, null, "jar" ); + Artifact artifact = repositorySystem.createPluginArtifact( pp ); if ( artifact != null ) { - extensionArtifacts.add( artifact ); + reportArtifacts.add( artifact ); } } - } - project.setExtensionArtifacts( extensionArtifacts ); + project.setReportArtifacts( reportArtifacts ); - // managedVersionMap - Map map = null; - if ( repositorySystem != null ) - { - final DependencyManagement dependencyManagement = project.getDependencyManagement(); - if ( ( dependencyManagement != null ) && ( ( dependencyManagement.getDependencies() ) != null ) - && ( dependencyManagement.getDependencies().size() > 0 ) ) + // extensionArtifacts + Set extensionArtifacts = new HashSet<>(); + List extensions = project.getBuildExtensions(); + if ( extensions != null ) { - map = new AbstractMap() + for ( Extension ext : extensions ) { - HashMap delegate; - - @Override - public Set> entrySet() + String version; + if ( StringUtils.isEmpty( ext.getVersion() ) ) { - return Collections.unmodifiableSet( compute().entrySet() ); + version = "RELEASE"; } - - @Override - public Set keySet() + else { - return Collections.unmodifiableSet( compute().keySet() ); + version = ext.getVersion(); } - @Override - public Collection values() - { - return Collections.unmodifiableCollection( compute().values() ); - } + Artifact artifact = repositorySystem.createArtifact( + ext.getGroupId(), ext.getArtifactId(), version, null, "jar" ); - @Override - public boolean containsValue( Object value ) + if ( artifact != null ) { - return compute().containsValue( value ); + extensionArtifacts.add( artifact ); } + } + } + project.setExtensionArtifacts( extensionArtifacts ); - @Override - public boolean containsKey( Object key ) + // managedVersionMap + Map map = null; + if ( repositorySystem != null ) + { + final DependencyManagement dependencyManagement = project.getDependencyManagement(); + if ( ( dependencyManagement != null ) && ( ( dependencyManagement.getDependencies() ) != null ) + && ( dependencyManagement.getDependencies().size() > 0 ) ) + { + map = new AbstractMap() { - return compute().containsKey( key ); - } + HashMap delegate; - @Override - public Artifact get( Object key ) - { - return compute().get( key ); - } + @Override + public Set> entrySet() + { + return Collections.unmodifiableSet( compute().entrySet() ); + } - HashMap compute() - { - if ( delegate == null ) + @Override + public Set keySet() { - delegate = new HashMap<>(); - for ( Dependency d : dependencyManagement.getDependencies() ) - { - Artifact artifact = repositorySystem.createDependencyArtifact( d ); + return Collections.unmodifiableSet( compute().keySet() ); + } - if ( artifact != null ) + @Override + public Collection values() + { + return Collections.unmodifiableCollection( compute().values() ); + } + + @Override + public boolean containsValue( Object value ) + { + return compute().containsValue( value ); + } + + @Override + public boolean containsKey( Object key ) + { + return compute().containsKey( key ); + } + + @Override + public Artifact get( Object key ) + { + return compute().get( key ); + } + + HashMap compute() + { + if ( delegate == null ) + { + delegate = new HashMap<>(); + for ( Dependency d : dependencyManagement.getDependencies() ) { - delegate.put( d.getManagementKey(), artifact ); + Artifact artifact = repositorySystem.createDependencyArtifact( d ); + + if ( artifact != null ) + { + delegate.put( d.getManagementKey(), artifact ); + } } } - } - return delegate; - } - }; - } - else - { - map = Collections.emptyMap(); + return delegate; + } + }; + } + else + { + map = Collections.emptyMap(); + } } - } - project.setManagedVersionMap( map ); + project.setManagedVersionMap( map ); - // release artifact repository - if ( project.getDistributionManagement() != null - && project.getDistributionManagement().getRepository() != null ) - { - try + // release artifact repository + if ( project.getDistributionManagement() != null + && project.getDistributionManagement().getRepository() != null ) { - DeploymentRepository r = project.getDistributionManagement().getRepository(); - if ( !StringUtils.isEmpty( r.getId() ) && !StringUtils.isEmpty( r.getUrl() ) ) + try { - ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository( r ); - repositorySystem.injectProxy( projectBuildingRequest.getRepositorySession(), - Arrays.asList( repo ) ); - repositorySystem.injectAuthentication( projectBuildingRequest.getRepositorySession(), - Arrays.asList( repo ) ); - project.setReleaseArtifactRepository( repo ); + DeploymentRepository r = project.getDistributionManagement().getRepository(); + if ( !StringUtils.isEmpty( r.getId() ) && !StringUtils.isEmpty( r.getUrl() ) ) + { + ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository( r ); + repositorySystem.injectProxy( projectBuildingRequest.getRepositorySession(), + Arrays.asList( repo ) ); + repositorySystem.injectAuthentication( projectBuildingRequest.getRepositorySession(), + Arrays.asList( repo ) ); + project.setReleaseArtifactRepository( repo ); + } + } + catch ( InvalidRepositoryException e ) + { + throw new IllegalStateException( "Failed to create release distribution repository for " + + project.getId(), e ); } } - catch ( InvalidRepositoryException e ) - { - throw new IllegalStateException( "Failed to create release distribution repository for " - + project.getId(), e ); - } - } - // snapshot artifact repository - if ( project.getDistributionManagement() != null - && project.getDistributionManagement().getSnapshotRepository() != null ) - { - try + // snapshot artifact repository + if ( project.getDistributionManagement() != null + && project.getDistributionManagement().getSnapshotRepository() != null ) { - DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository(); - if ( !StringUtils.isEmpty( r.getId() ) && !StringUtils.isEmpty( r.getUrl() ) ) + try { - ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository( r ); - repositorySystem.injectProxy( projectBuildingRequest.getRepositorySession(), - Arrays.asList( repo ) ); - repositorySystem.injectAuthentication( projectBuildingRequest.getRepositorySession(), - Arrays.asList( repo ) ); - project.setSnapshotArtifactRepository( repo ); + DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository(); + if ( !StringUtils.isEmpty( r.getId() ) && !StringUtils.isEmpty( r.getUrl() ) ) + { + ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository( r ); + repositorySystem.injectProxy( projectBuildingRequest.getRepositorySession(), + Arrays.asList( repo ) ); + repositorySystem.injectAuthentication( projectBuildingRequest.getRepositorySession(), + Arrays.asList( repo ) ); + project.setSnapshotArtifactRepository( repo ); + } + } + catch ( InvalidRepositoryException e ) + { + throw new IllegalStateException( "Failed to create snapshot distribution repository for " + + project.getId(), e ); } - } - catch ( InvalidRepositoryException e ) - { - throw new IllegalStateException( "Failed to create snapshot distribution repository for " - + project.getId(), e ); } } - } - - private void initParent( MavenProject project, Map projects, boolean buildParentIfNotExisting, - ModelBuildingResult result, ProjectBuildingRequest projectBuildingRequest ) - { - Model parentModel = result.getModelIds().size() > 1 && !result.getModelIds().get( 1 ).isEmpty() - ? result.getRawModel( result.getModelIds().get( 1 ) ) - : null; - if ( parentModel != null ) + private void initParent( MavenProject project, Map projects, + boolean buildParentIfNotExisting, ModelBuildingResult result, + ProjectBuildingRequest projectBuildingRequest ) { - final String parentGroupId = inheritedGroupId( result, 1 ); - final String parentVersion = inheritedVersion( result, 1 ); - - project.setParentArtifact( repositorySystem.createProjectArtifact( parentGroupId, - parentModel.getArtifactId(), - parentVersion ) ); - - // org.apache.maven.its.mng4834:parent:0.1 - String parentModelId = result.getModelIds().get( 1 ); - File parentPomFile = result.getRawModel( parentModelId ).getPomFile(); - MavenProject parent = projects.get( parentPomFile ); - if ( parent == null && buildParentIfNotExisting ) + Model parentModel = result.getModelIds().size() > 1 && !result.getModelIds().get( 1 ).isEmpty() + ? result.getRawModel( result.getModelIds().get( 1 ) ) + : null; + + if ( parentModel != null ) { - // - // At this point the DefaultModelBuildingListener has fired and it populates the - // remote repositories with those found in the pom.xml, along with the existing externally - // defined repositories. - // - projectBuildingRequest.setRemoteRepositories( project.getRemoteArtifactRepositories() ); - if ( parentPomFile != null ) + final String parentGroupId = inheritedGroupId( result, 1 ); + final String parentVersion = inheritedVersion( result, 1 ); + + project.setParentArtifact( repositorySystem.createProjectArtifact( parentGroupId, + parentModel.getArtifactId(), + parentVersion ) ); + + // org.apache.maven.its.mng4834:parent:0.1 + String parentModelId = result.getModelIds().get( 1 ); + File parentPomFile = result.getRawModel( parentModelId ).getPomFile(); + MavenProject parent = projects.get( parentPomFile ); + if ( parent == null && buildParentIfNotExisting ) { - project.setParentFile( parentPomFile ); - try - { - parent = build( parentPomFile, projectBuildingRequest ).getProject(); - } - catch ( ProjectBuildingException e ) + // + // At this point the DefaultModelBuildingListener has fired and it populates the + // remote repositories with those found in the pom.xml, along with the existing externally + // defined repositories. + // + projectBuildingRequest.setRemoteRepositories( project.getRemoteArtifactRepositories() ); + if ( parentPomFile != null ) { - // MNG-4488 where let invalid parents slide on by - if ( logger.isDebugEnabled() ) + project.setParentFile( parentPomFile ); + try { - // Message below is checked for in the MNG-2199 core IT. - logger.warn( "Failed to build parent project for " + project.getId(), e ); + parent = build( parentPomFile, new FileModelSource( parentPomFile ), + new InternalConfig( projectBuildingRequest, null, null ) ).getProject(); } - else + catch ( ProjectBuildingException e ) { - // Message below is checked for in the MNG-2199 core IT. - logger.warn( "Failed to build parent project for " + project.getId() ); + // MNG-4488 where let invalid parents slide on by + if ( logger.isDebugEnabled() ) + { + // Message below is checked for in the MNG-2199 core IT. + logger.warn( "Failed to build parent project for " + project.getId(), e ); + } + else + { + // Message below is checked for in the MNG-2199 core IT. + logger.warn( "Failed to build parent project for " + project.getId() ); + } } } - } - else - { - Artifact parentArtifact = project.getParentArtifact(); - try + else { - parent = build( parentArtifact, projectBuildingRequest ).getProject(); - } - catch ( ProjectBuildingException e ) - { - // MNG-4488 where let invalid parents slide on by - if ( logger.isDebugEnabled() ) + Artifact parentArtifact = project.getParentArtifact(); + try { - // Message below is checked for in the MNG-2199 core IT. - logger.warn( "Failed to build parent project for " + project.getId(), e ); + parent = build( parentArtifact, false, projectBuildingRequest ).getProject(); } - else + catch ( ProjectBuildingException e ) { - // Message below is checked for in the MNG-2199 core IT. - logger.warn( "Failed to build parent project for " + project.getId() ); + // MNG-4488 where let invalid parents slide on by + if ( logger.isDebugEnabled() ) + { + // Message below is checked for in the MNG-2199 core IT. + logger.warn( "Failed to build parent project for " + project.getId(), e ); + } + else + { + // Message below is checked for in the MNG-2199 core IT. + logger.warn( "Failed to build parent project for " + project.getId() ); + } } } } + project.setParent( parent ); + if ( project.getParentFile() == null && parent != null ) + { + project.setParentFile( parent.getFile() ); + } } - project.setParent( parent ); - if ( project.getParentFile() == null && parent != null ) + } + + private ModelBuildingRequest getModelBuildingRequest( InternalConfig config ) + { + ProjectBuildingRequest configuration = config.request; + + ModelBuildingRequest request = new DefaultModelBuildingRequest(); + + RequestTrace trace = RequestTrace.newChild( null, configuration ).newChild( request ); + + ModelResolver resolver = + new ProjectModelResolver( config.session, trace, repoSystem, repositoryManager, config.repositories, + configuration.getRepositoryMerging(), config.modelPool ); + + request.setValidationLevel( configuration.getValidationLevel() ); + request.setProcessPlugins( configuration.isProcessPlugins() ); + request.setProfiles( configuration.getProfiles() ); + request.setActiveProfileIds( configuration.getActiveProfileIds() ); + request.setInactiveProfileIds( configuration.getInactiveProfileIds() ); + request.setSystemProperties( configuration.getSystemProperties() ); + request.setUserProperties( configuration.getUserProperties() ); + request.setBuildStartTime( configuration.getBuildStartTime() ); + request.setModelResolver( resolver ); + // this is a hint that we want to build 1 file, so don't cache. See MNG-7063 + if ( config.modelPool != null ) { - project.setParentFile( parent.getFile() ); + request.setModelCache( modelCacheFactory.createCache( config.session ) ); } + request.setTransformerContextBuilder( config.transformerContextBuilder ); + + return request; } + + /** + * InternalConfig + */ + class InternalConfig + { + + private final ProjectBuildingRequest request; + + private final RepositorySystemSession session; + + private final List repositories; + + private final ReactorModelPool modelPool; + + private final TransformerContextBuilder transformerContextBuilder; + + InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, + TransformerContextBuilder transformerContextBuilder ) + { + this.request = request; + this.modelPool = modelPool; + this.transformerContextBuilder = transformerContextBuilder; + + session = + LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(), + repoSystem ); + repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); + + } + + } + + } + + private DependencyResolutionResult resolveDependencies( MavenProject project, RepositorySystemSession session ) + { + DependencyResolutionResult resolutionResult; + + try + { + DefaultDependencyResolutionRequest resolution = new DefaultDependencyResolutionRequest( project, session ); + resolutionResult = dependencyResolver.resolve( resolution ); + } + catch ( DependencyResolutionException e ) + { + resolutionResult = e.getResult(); + } + + Set artifacts = new LinkedHashSet<>(); + if ( resolutionResult.getDependencyGraph() != null ) + { + RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(), + Collections.singletonList( project.getArtifact().getId() ), null ); + + // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not + LocalRepositoryManager lrm = session.getLocalRepositoryManager(); + for ( Artifact artifact : artifacts ) + { + if ( !artifact.isResolved() ) + { + String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) ); + artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); + } + } + } + project.setResolvedArtifacts( artifacts ); + project.setArtifacts( artifacts ); + + return resolutionResult; + } + + private List getProfileIds( List profiles ) + { + return profiles.stream().map( org.apache.maven.model.Profile::getId ).collect( Collectors.toList() ); + } + + private ModelSource createStubModelSource( Artifact artifact ) + { + StringBuilder buffer = new StringBuilder( 1024 ); + + buffer.append( "" ); + buffer.append( "" ); + buffer.append( "4.0.0" ); + buffer.append( "" ).append( artifact.getGroupId() ).append( "" ); + buffer.append( "" ).append( artifact.getArtifactId() ).append( "" ); + buffer.append( "" ).append( artifact.getBaseVersion() ).append( "" ); + buffer.append( "" ).append( artifact.getType() ).append( "" ); + buffer.append( "" ); + + return new StringModelSource( buffer, artifact.getId() ); } private static String inheritedGroupId( final ModelBuildingResult result, final int modelIndex ) @@ -1009,36 +1066,4 @@ private static String inheritedVersion( final ModelBuildingResult result, final return version; } - /** - * InternalConfig - */ - class InternalConfig - { - - private final ProjectBuildingRequest request; - - private final RepositorySystemSession session; - - private final List repositories; - - private final ReactorModelPool modelPool; - - private final TransformerContextBuilder transformerContextBuilder; - - InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, - TransformerContextBuilder transformerContextBuilder ) - { - this.request = request; - this.modelPool = modelPool; - this.transformerContextBuilder = transformerContextBuilder; - - session = - LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(), - repoSystem ); - repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); - - } - - } - } From 8786ea4b7b461e81c0d79ea123d8f9f8d6562a88 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 13 Sep 2022 18:04:52 +0200 Subject: [PATCH 02/16] Inline InternalConfig into BuildSession --- .../maven/project/DefaultProjectBuilder.java | 286 ++++++++---------- 1 file changed, 125 insertions(+), 161 deletions(-) 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 4cb2c50c7234..6fee95e04bf3 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 @@ -133,14 +133,14 @@ public DefaultProjectBuilder( public ProjectBuildingResult build( File pomFile, ProjectBuildingRequest request ) throws ProjectBuildingException { - return new BuildSession().build( pomFile, new FileModelSource( pomFile ), request ); + return new BuildSession( request, false ).build( pomFile, new FileModelSource( pomFile ) ); } @Override public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request ) throws ProjectBuildingException { - return new BuildSession().build( null, modelSource, request ); + return new BuildSession( request, false ).build( null, modelSource ); } @Override @@ -154,14 +154,14 @@ public ProjectBuildingResult build( Artifact artifact, ProjectBuildingRequest re public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request ) throws ProjectBuildingException { - return new BuildSession().build( artifact, allowStubModel, request ); + return new BuildSession( request, false ).build( artifact, allowStubModel ); } @Override public List build( List pomFiles, boolean recursive, ProjectBuildingRequest request ) throws ProjectBuildingException { - return new BuildSession().build( pomFiles, recursive, request ); + return new BuildSession( request, true ).build( pomFiles, recursive ); } static class InterimResult @@ -193,46 +193,64 @@ static class InterimResult class BuildSession { - ProjectBuildingResult build( File pomFile, ModelSource modelSource, ProjectBuildingRequest request ) - throws ProjectBuildingException + private final ProjectBuildingRequest request; + private final RepositorySystemSession session; + private final List repositories; + private final ReactorModelPool.Builder poolBuilder; + private final ReactorModelPool modelPool; + private final TransformerContextBuilder transformerContextBuilder; + + BuildSession( ProjectBuildingRequest request, boolean localProjects ) { - InternalConfig config = new InternalConfig( request, null, null ); - return build( pomFile, modelSource, config ); + this.request = request; + this.session = LegacyLocalRepositoryManager.overlay( + request.getLocalRepository(), request.getRepositorySession(), repoSystem ); + this.repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); + if ( localProjects ) + { + this.poolBuilder = new ReactorModelPool.Builder(); + this.modelPool = poolBuilder.build(); + this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder(); + } + else + { + this.poolBuilder = null; + this.modelPool = null; + this.transformerContextBuilder = null; + } } - ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConfig config ) + ProjectBuildingResult build( File pomFile, ModelSource modelSource ) throws ProjectBuildingException { ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); try { - ProjectBuildingRequest projectBuildingRequest = config.request; - - MavenProject project = projectBuildingRequest.getProject(); + MavenProject project = request.getProject(); List modelProblems = null; Throwable error = null; if ( project == null ) { - ModelBuildingRequest request = getModelBuildingRequest( config ); + ModelBuildingRequest modelBuildingRequest = getModelBuildingRequest(); project = new MavenProject(); project.setFile( pomFile ); DefaultModelBuildingListener listener = - new DefaultModelBuildingListener( project, projectBuildingHelper, projectBuildingRequest ); - request.setModelBuildingListener( listener ); + new DefaultModelBuildingListener( project, projectBuildingHelper, request ); + modelBuildingRequest.setModelBuildingListener( listener ); - request.setPomFile( pomFile ); - request.setModelSource( modelSource ); - request.setLocationTracking( true ); + modelBuildingRequest.setPomFile( pomFile ); + modelBuildingRequest.setModelSource( modelSource ); + modelBuildingRequest.setLocationTracking( true ); ModelBuildingResult result; try { - result = modelBuilder.build( request ); + result = modelBuilder.build( modelBuildingRequest ); } catch ( ModelBuildingException e ) { @@ -247,19 +265,18 @@ ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConf modelProblems = result.getProblems(); - initProject( project, Collections.emptyMap(), true, - result, projectBuildingRequest ); + initProject( project, Collections.emptyMap(), result ); } - else if ( projectBuildingRequest.isResolveDependencies() ) + else if ( request.isResolveDependencies() ) { projectBuildingHelper.selectProjectRealm( project ); } DependencyResolutionResult resolutionResult = null; - if ( projectBuildingRequest.isResolveDependencies() ) + if ( request.isResolveDependencies() ) { - resolutionResult = resolveDependencies( project, config.session ); + resolutionResult = resolveDependencies( project ); } ProjectBuildingResult result = @@ -280,23 +297,20 @@ else if ( projectBuildingRequest.isResolveDependencies() ) } } - ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request ) + ProjectBuildingResult build( Artifact artifact, boolean allowStubModel ) throws ProjectBuildingException { org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact ); pomArtifact = ArtifactDescriptorUtils.toPomArtifact( pomArtifact ); - InternalConfig config = - new InternalConfig( request, null, null ); - boolean localProject; try { ArtifactRequest pomRequest = new ArtifactRequest(); pomRequest.setArtifact( pomArtifact ); - pomRequest.setRepositories( config.repositories ); - ArtifactResult pomResult = repoSystem.resolveArtifact( config.session, pomRequest ); + pomRequest.setRepositories( repositories ); + ArtifactResult pomResult = repoSystem.resolveArtifact( session, pomRequest ); pomArtifact = pomResult.getArtifact(); localProject = pomResult.getRepository() instanceof WorkspaceRepository; @@ -305,7 +319,7 @@ ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectB { if ( e.getResults().get( 0 ).isMissing() && allowStubModel ) { - return build( null, createStubModelSource( artifact ), config ); + return build( null, createStubModelSource( artifact ) ); } throw new ProjectBuildingException( artifact.getId(), "Error resolving project artifact: " + e.getMessage(), e ); @@ -322,45 +336,36 @@ ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectB if ( localProject ) { - return build( pomFile, new FileModelSource( pomFile ), config ); + return build( pomFile, new FileModelSource( pomFile ) ); } else { - return build( null, new ArtifactModelSource( pomFile, artifact.getGroupId(), artifact.getArtifactId(), - artifact.getVersion() ), - config ); + return build( null, new ArtifactModelSource( + pomFile, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() ) ); } } - List build( List pomFiles, boolean recursive, ProjectBuildingRequest request ) + List build( List pomFiles, boolean recursive ) throws ProjectBuildingException { List results = new ArrayList<>(); List interimResults = new ArrayList<>(); - ReactorModelPool.Builder poolBuilder = new ReactorModelPool.Builder(); - final ReactorModelPool modelPool = poolBuilder.build(); - - InternalConfig config = - new InternalConfig( request, modelPool, modelBuilder.newTransformerContextBuilder() ); - 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 ); + poolBuilder ); ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); try { // Phase 2: get effective models from the reactor - noErrors = - build( results, new ArrayList<>(), projectIndex, interimResults, request, - config.session ) && noErrors; + noErrors = build( results, new ArrayList<>(), projectIndex, interimResults ) && noErrors; } finally { @@ -370,7 +375,7 @@ List build( List pomFiles, boolean recursive, Proje if ( Features.buildConsumer( request.getUserProperties() ).isActive() ) { request.getRepositorySession().getData().set( TransformerContext.KEY, - config.transformerContextBuilder.build() ); + transformerContextBuilder.build() ); } if ( !noErrors ) @@ -384,7 +389,7 @@ List build( List pomFiles, boolean recursive, Proje @SuppressWarnings( "checkstyle:parameternumber" ) private boolean build( List results, List interimResults, Map projectIndex, List pomFiles, Set aggregatorFiles, - boolean root, boolean recursive, InternalConfig config, + boolean root, boolean recursive, ReactorModelPool.Builder poolBuilder ) { boolean noErrors = true; @@ -393,7 +398,7 @@ private boolean build( List results, List { aggregatorFiles.add( pomFile ); - if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, config, + if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, poolBuilder ) ) { noErrors = false; @@ -408,7 +413,7 @@ private boolean build( List results, List @SuppressWarnings( "checkstyle:parameternumber" ) private boolean build( List results, List interimResults, Map projectIndex, File pomFile, Set aggregatorFiles, - boolean isRoot, boolean recursive, InternalConfig config, + boolean isRoot, boolean recursive, ReactorModelPool.Builder poolBuilder ) { boolean noErrors = true; @@ -416,19 +421,19 @@ private boolean build( List results, List MavenProject project = new MavenProject(); project.setFile( pomFile ); - ModelBuildingRequest request = getModelBuildingRequest( config ) + ModelBuildingRequest modelBuildingRequest = getModelBuildingRequest() .setPomFile( pomFile ) .setTwoPhaseBuilding( true ) .setLocationTracking( true ); DefaultModelBuildingListener listener = - new DefaultModelBuildingListener( project, projectBuildingHelper, config.request ); - request.setModelBuildingListener( listener ); + new DefaultModelBuildingListener( project, projectBuildingHelper, request ); + modelBuildingRequest.setModelBuildingListener( listener ); ModelBuildingResult result; try { - result = modelBuilder.build( request ); + result = modelBuilder.build( modelBuildingRequest ); } catch ( ModelBuildingException e ) { @@ -448,7 +453,7 @@ private boolean build( List results, List poolBuilder.put( model.getPomFile().toPath(), model ); - InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot ); + InterimResult interimResult = new InterimResult( pomFile, modelBuildingRequest, result, listener, isRoot ); interimResults.add( interimResult ); if ( recursive ) @@ -527,7 +532,7 @@ private boolean build( List results, List interimResult.modules = new ArrayList<>(); if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false, - recursive, config, poolBuilder ) ) + recursive, poolBuilder ) ) { noErrors = false; } @@ -539,9 +544,7 @@ private boolean build( List results, List } private boolean build( List results, List projects, - Map projectIndex, List interimResults, - ProjectBuildingRequest request, - RepositorySystemSession session ) + Map projectIndex, List interimResults ) { boolean noErrors = true; @@ -555,7 +558,7 @@ private boolean build( List results, List p // 2nd pass of initialization: resolve and build parent if necessary try { - initProject( project, projectIndex, true, result, request ); + initProject( project, projectIndex, result ); } catch ( InvalidArtifactRTException iarte ) { @@ -564,8 +567,7 @@ private boolean build( List results, List p } List modules = new ArrayList<>(); - noErrors = build( results, modules, projectIndex, interimResult.modules, request, session ) - && noErrors; + noErrors = build( results, modules, projectIndex, interimResult.modules ) && noErrors; projects.addAll( modules ); projects.add( project ); @@ -575,7 +577,7 @@ private boolean build( List results, List p DependencyResolutionResult resolutionResult = null; if ( request.isResolveDependencies() ) { - resolutionResult = resolveDependencies( project, session ); + resolutionResult = resolveDependencies( project ); } results.add( new DefaultProjectBuildingResult( project, result.getProblems(), resolutionResult ) ); @@ -603,14 +605,12 @@ private boolean build( List results, List p } @SuppressWarnings( "checkstyle:methodlength" ) - private void initProject( MavenProject project, Map projects, - boolean buildParentIfNotExisting, ModelBuildingResult result, - ProjectBuildingRequest projectBuildingRequest ) + private void initProject( MavenProject project, Map projects, ModelBuildingResult result ) { project.setModel( result.getEffectiveModel() ); project.setOriginalModel( result.getFileModel() ); - initParent( project, projects, buildParentIfNotExisting, result, projectBuildingRequest ); + initParent( project, projects, result ); Artifact projectArtifact = repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), @@ -618,7 +618,7 @@ private void initProject( MavenProject project, Map projects project.setArtifact( projectArtifact ); // only set those on 2nd phase, ignore on 1st pass - if ( project.getFile() != null && buildParentIfNotExisting ) + if ( project.getFile() != null ) { Build build = project.getBuild(); project.addScriptSourceRoot( build.getScriptSourceDirectory() ); @@ -641,7 +641,7 @@ private void initProject( MavenProject project, Map projects // All the parts that were taken out of MavenProject for Maven 4.0.0 // - project.setProjectBuildingRequest( projectBuildingRequest ); + project.setProjectBuildingRequest( request ); // pluginArtifacts Set pluginArtifacts = new HashSet<>(); @@ -787,9 +787,9 @@ HashMap compute() if ( !StringUtils.isEmpty( r.getId() ) && !StringUtils.isEmpty( r.getUrl() ) ) { ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository( r ); - repositorySystem.injectProxy( projectBuildingRequest.getRepositorySession(), + repositorySystem.injectProxy( request.getRepositorySession(), Arrays.asList( repo ) ); - repositorySystem.injectAuthentication( projectBuildingRequest.getRepositorySession(), + repositorySystem.injectAuthentication( request.getRepositorySession(), Arrays.asList( repo ) ); project.setReleaseArtifactRepository( repo ); } @@ -811,9 +811,9 @@ HashMap compute() if ( !StringUtils.isEmpty( r.getId() ) && !StringUtils.isEmpty( r.getUrl() ) ) { ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository( r ); - repositorySystem.injectProxy( projectBuildingRequest.getRepositorySession(), + repositorySystem.injectProxy( request.getRepositorySession(), Arrays.asList( repo ) ); - repositorySystem.injectAuthentication( projectBuildingRequest.getRepositorySession(), + repositorySystem.injectAuthentication( request.getRepositorySession(), Arrays.asList( repo ) ); project.setSnapshotArtifactRepository( repo ); } @@ -826,9 +826,7 @@ HashMap compute() } } - private void initParent( MavenProject project, Map projects, - boolean buildParentIfNotExisting, ModelBuildingResult result, - ProjectBuildingRequest projectBuildingRequest ) + private void initParent( MavenProject project, Map projects, ModelBuildingResult result ) { Model parentModel = result.getModelIds().size() > 1 && !result.getModelIds().get( 1 ).isEmpty() ? result.getRawModel( result.getModelIds().get( 1 ) ) @@ -847,21 +845,20 @@ private void initParent( MavenProject project, Map projects, String parentModelId = result.getModelIds().get( 1 ); File parentPomFile = result.getRawModel( parentModelId ).getPomFile(); MavenProject parent = projects.get( parentPomFile ); - if ( parent == null && buildParentIfNotExisting ) + if ( parent == null ) { // // At this point the DefaultModelBuildingListener has fired and it populates the // remote repositories with those found in the pom.xml, along with the existing externally // defined repositories. // - projectBuildingRequest.setRemoteRepositories( project.getRemoteArtifactRepositories() ); + request.setRemoteRepositories( project.getRemoteArtifactRepositories() ); if ( parentPomFile != null ) { project.setParentFile( parentPomFile ); try { - parent = build( parentPomFile, new FileModelSource( parentPomFile ), - new InternalConfig( projectBuildingRequest, null, null ) ).getProject(); + parent = build( parentPomFile, new FileModelSource( parentPomFile ) ).getProject(); } catch ( ProjectBuildingException e ) { @@ -883,7 +880,7 @@ private void initParent( MavenProject project, Map projects, Artifact parentArtifact = project.getParentArtifact(); try { - parent = build( parentArtifact, false, projectBuildingRequest ).getProject(); + parent = build( parentArtifact, false ).getProject(); } catch ( ProjectBuildingException e ) { @@ -909,106 +906,73 @@ private void initParent( MavenProject project, Map projects, } } - private ModelBuildingRequest getModelBuildingRequest( InternalConfig config ) + private ModelBuildingRequest getModelBuildingRequest() { - ProjectBuildingRequest configuration = config.request; - - ModelBuildingRequest request = new DefaultModelBuildingRequest(); + ModelBuildingRequest modelBuildingRequest = new DefaultModelBuildingRequest(); - RequestTrace trace = RequestTrace.newChild( null, configuration ).newChild( request ); + RequestTrace trace = RequestTrace.newChild( null, request ).newChild( modelBuildingRequest ); ModelResolver resolver = - new ProjectModelResolver( config.session, trace, repoSystem, repositoryManager, config.repositories, - configuration.getRepositoryMerging(), config.modelPool ); - - request.setValidationLevel( configuration.getValidationLevel() ); - request.setProcessPlugins( configuration.isProcessPlugins() ); - request.setProfiles( configuration.getProfiles() ); - request.setActiveProfileIds( configuration.getActiveProfileIds() ); - request.setInactiveProfileIds( configuration.getInactiveProfileIds() ); - request.setSystemProperties( configuration.getSystemProperties() ); - request.setUserProperties( configuration.getUserProperties() ); - request.setBuildStartTime( configuration.getBuildStartTime() ); - request.setModelResolver( resolver ); + new ProjectModelResolver( session, trace, repoSystem, repositoryManager, repositories, + request.getRepositoryMerging(), modelPool ); + + modelBuildingRequest.setValidationLevel( request.getValidationLevel() ); + modelBuildingRequest.setProcessPlugins( request.isProcessPlugins() ); + modelBuildingRequest.setProfiles( request.getProfiles() ); + modelBuildingRequest.setActiveProfileIds( request.getActiveProfileIds() ); + modelBuildingRequest.setInactiveProfileIds( request.getInactiveProfileIds() ); + modelBuildingRequest.setSystemProperties( request.getSystemProperties() ); + modelBuildingRequest.setUserProperties( request.getUserProperties() ); + modelBuildingRequest.setBuildStartTime( request.getBuildStartTime() ); + modelBuildingRequest.setModelResolver( resolver ); // this is a hint that we want to build 1 file, so don't cache. See MNG-7063 - if ( config.modelPool != null ) + if ( modelPool != null ) { - request.setModelCache( modelCacheFactory.createCache( config.session ) ); + modelBuildingRequest.setModelCache( modelCacheFactory.createCache( session ) ); } - request.setTransformerContextBuilder( config.transformerContextBuilder ); + modelBuildingRequest.setTransformerContextBuilder( transformerContextBuilder ); - return request; + return modelBuildingRequest; } - /** - * InternalConfig - */ - class InternalConfig + private DependencyResolutionResult resolveDependencies( MavenProject project ) { + DependencyResolutionResult resolutionResult; - private final ProjectBuildingRequest request; - - private final RepositorySystemSession session; - - private final List repositories; - - private final ReactorModelPool modelPool; - - private final TransformerContextBuilder transformerContextBuilder; - - InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, - TransformerContextBuilder transformerContextBuilder ) + try { - this.request = request; - this.modelPool = modelPool; - this.transformerContextBuilder = transformerContextBuilder; - - session = - LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(), - repoSystem ); - repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); - + DefaultDependencyResolutionRequest resolution = + new DefaultDependencyResolutionRequest( project, session ); + resolutionResult = dependencyResolver.resolve( resolution ); + } + catch ( DependencyResolutionException e ) + { + resolutionResult = e.getResult(); } - } - - } - - private DependencyResolutionResult resolveDependencies( MavenProject project, RepositorySystemSession session ) - { - DependencyResolutionResult resolutionResult; - - try - { - DefaultDependencyResolutionRequest resolution = new DefaultDependencyResolutionRequest( project, session ); - resolutionResult = dependencyResolver.resolve( resolution ); - } - catch ( DependencyResolutionException e ) - { - resolutionResult = e.getResult(); - } - - Set artifacts = new LinkedHashSet<>(); - if ( resolutionResult.getDependencyGraph() != null ) - { - RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(), - Collections.singletonList( project.getArtifact().getId() ), null ); - - // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not - LocalRepositoryManager lrm = session.getLocalRepositoryManager(); - for ( Artifact artifact : artifacts ) + Set artifacts = new LinkedHashSet<>(); + if ( resolutionResult.getDependencyGraph() != null ) { - if ( !artifact.isResolved() ) + RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(), + Collections.singletonList( project.getArtifact().getId() ), null ); + + // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not + LocalRepositoryManager lrm = session.getLocalRepositoryManager(); + for ( Artifact artifact : artifacts ) { - String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) ); - artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); + if ( !artifact.isResolved() ) + { + String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) ); + artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); + } } } + project.setResolvedArtifacts( artifacts ); + project.setArtifacts( artifacts ); + + return resolutionResult; } - project.setResolvedArtifacts( artifacts ); - project.setArtifacts( artifacts ); - return resolutionResult; } private List getProfileIds( List profiles ) @@ -1016,7 +980,7 @@ private List getProfileIds( List profile return profiles.stream().map( org.apache.maven.model.Profile::getId ).collect( Collectors.toList() ); } - private ModelSource createStubModelSource( Artifact artifact ) + private static ModelSource createStubModelSource( Artifact artifact ) { StringBuilder buffer = new StringBuilder( 1024 ); From 1617c9bf9b1cae4f4be37c67c275dc75ca0ae829 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 14 Sep 2022 09:04:21 +0200 Subject: [PATCH 03/16] Introduce multi-threading computation in DefaultProjectBuilder using a ForkJoinPool --- .../maven/project/DefaultProjectBuilder.java | 232 ++++++++++-------- .../project/ProjectBuildingException.java | 16 ++ .../maven/project/ReactorModelPool.java | 6 +- 3 files changed, 146 insertions(+), 108 deletions(-) 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 6fee95e04bf3..eb0dd60ca8ac 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 @@ -36,6 +36,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; import java.util.stream.Collectors; import org.apache.maven.RepositoryUtils; @@ -179,6 +182,8 @@ static class InterimResult List modules = Collections.emptyList(); + ProjectBuildingResult projectBuildingResult; + InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result, DefaultModelBuildingListener listener, boolean root ) { @@ -189,6 +194,10 @@ static class InterimResult this.root = root; } + InterimResult( ProjectBuildingResult projectBuildingResult ) + { + this.projectBuildingResult = projectBuildingResult; + } } class BuildSession @@ -199,6 +208,7 @@ class BuildSession private final ReactorModelPool.Builder poolBuilder; private final ReactorModelPool modelPool; private final TransformerContextBuilder transformerContextBuilder; + private final ForkJoinPool forkJoinPool; BuildSession( ProjectBuildingRequest request, boolean localProjects ) { @@ -211,12 +221,14 @@ class BuildSession this.poolBuilder = new ReactorModelPool.Builder(); this.modelPool = poolBuilder.build(); this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder(); + this.forkJoinPool = new ForkJoinPool(); } else { this.poolBuilder = null; this.modelPool = null; this.transformerContextBuilder = null; + this.forkJoinPool = null; } } @@ -349,75 +361,88 @@ ProjectBuildingResult build( Artifact artifact, boolean allowStubModel ) List build( List pomFiles, boolean recursive ) throws ProjectBuildingException { - List results = new ArrayList<>(); + try + { + List results = + forkJoinPool.submit( () -> doBuild( pomFiles, recursive ) ).join(); - List interimResults = new ArrayList<>(); + if ( results.stream().flatMap( r -> r.getProblems().stream() ) + .anyMatch( p -> p.getSeverity() != ModelProblem.Severity.WARNING ) ) + { + throw new ProjectBuildingException( results ); + } - Map projectIndex = new HashMap<>( 256 ); + return results; + } + catch ( Exception e ) + { + for ( Throwable t = e; t != null; t = t.getCause() ) + { + if ( t instanceof ProjectBuildingException ) + { + throw ( ProjectBuildingException ) t; + } + } + throw new RuntimeException( e ); + } + } + + List doBuild( List pomFiles, boolean recursive ) + { + Map projectIndex = new ConcurrentHashMap<>( 256 ); // phase 1: get file Models from the reactor. - boolean noErrors = - build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive, - poolBuilder ); + List interimResults = build( + projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive ); ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); try { // Phase 2: get effective models from the reactor - noErrors = build( results, new ArrayList<>(), projectIndex, interimResults ) && noErrors; + List results = build( projectIndex, interimResults ); + + if ( Features.buildConsumer( request.getUserProperties() ).isActive() ) + { + request.getRepositorySession().getData().set( TransformerContext.KEY, + transformerContextBuilder.build() ); + } + + return results; } finally { Thread.currentThread().setContextClassLoader( oldContextClassLoader ); } - - if ( Features.buildConsumer( request.getUserProperties() ).isActive() ) - { - request.getRepositorySession().getData().set( TransformerContext.KEY, - transformerContextBuilder.build() ); - } - - if ( !noErrors ) - { - throw new ProjectBuildingException( results ); - } - - return results; } @SuppressWarnings( "checkstyle:parameternumber" ) - private boolean build( List results, List interimResults, + private List build( Map projectIndex, List pomFiles, Set aggregatorFiles, - boolean root, boolean recursive, - ReactorModelPool.Builder poolBuilder ) + boolean root, boolean recursive ) { - boolean noErrors = true; - - for ( File pomFile : pomFiles ) - { - aggregatorFiles.add( pomFile ); - - if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, root, recursive, - poolBuilder ) ) - { - noErrors = false; - } - - aggregatorFiles.remove( pomFile ); - } + List> tasks = pomFiles.stream().map( pomFile -> ForkJoinTask.adapt( + () -> build( projectIndex, pomFile, + concat( aggregatorFiles, pomFile ), root, recursive ) ) ) + .collect( Collectors.toList() ); + + return ForkJoinTask.invokeAll( tasks ).stream() + .map( ForkJoinTask::getRawResult ) + .collect( Collectors.toList() ); + } - return noErrors; + private Set concat( Set set, T elem ) + { + Set newSet = new HashSet<>( set ); + newSet.add( elem ); + return newSet; } @SuppressWarnings( "checkstyle:parameternumber" ) - private boolean build( List results, List interimResults, + private InterimResult build( Map projectIndex, File pomFile, Set aggregatorFiles, - boolean isRoot, boolean recursive, - ReactorModelPool.Builder poolBuilder ) + boolean isRoot, boolean recursive ) { - boolean noErrors = true; - MavenProject project = new MavenProject(); project.setFile( pomFile ); @@ -440,13 +465,11 @@ private boolean build( List results, List result = e.getResult(); if ( result == null || result.getFileModel() == null ) { - results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); - - return false; + return new InterimResult( + new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); } // validation error, continue project building and delay failing to help IDEs // result.getProblems().addAll(e.getProblems()) ? - noErrors = false; } Model model = result.getFileModel(); @@ -454,7 +477,6 @@ private boolean build( List results, List poolBuilder.put( model.getPomFile().toPath(), model ); InterimResult interimResult = new InterimResult( pomFile, modelBuildingRequest, result, listener, isRoot ); - interimResults.add( interimResult ); if ( recursive ) { @@ -484,8 +506,6 @@ private boolean build( List results, List model, -1, -1, null ); result.getProblems().add( problem ); - noErrors = false; - continue; } @@ -521,87 +541,89 @@ private boolean build( List results, List ModelProblem.Version.BASE, model, -1, -1, null ); result.getProblems().add( problem ); - noErrors = false; - continue; } moduleFiles.add( moduleFile ); } - interimResult.modules = new ArrayList<>(); - - if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false, - recursive, poolBuilder ) ) + if ( !moduleFiles.isEmpty() ) { - noErrors = false; + interimResult.modules = build( projectIndex, moduleFiles, aggregatorFiles, false, recursive ); } } projectIndex.put( pomFile, project ); - return noErrors; + return interimResult; } - private boolean build( List results, List projects, - Map projectIndex, List interimResults ) + private List build( Map projectIndex, + List interimResults ) { - boolean noErrors = true; + List>> tasks = interimResults.stream().map( interimResult -> + ForkJoinTask.adapt( () -> doBuild( projectIndex, interimResult ) ) ) + .collect( Collectors.toList() ); + + return ForkJoinTask.invokeAll( tasks ).stream() + .map( ForkJoinTask::getRawResult ) + .flatMap( List::stream ) + .collect( Collectors.toList() ); + } - for ( InterimResult interimResult : interimResults ) + private List doBuild( Map projectIndex, InterimResult interimResult ) + { + if ( interimResult.projectBuildingResult != null ) { - MavenProject project = interimResult.listener.getProject(); + return Collections.singletonList( interimResult.projectBuildingResult ); + } + MavenProject project = interimResult.listener.getProject(); + try + { + ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result ); + + // 2nd pass of initialization: resolve and build parent if necessary try { - ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result ); - - // 2nd pass of initialization: resolve and build parent if necessary - try - { - initProject( project, projectIndex, result ); - } - catch ( InvalidArtifactRTException iarte ) - { - result.getProblems().add( new DefaultModelProblem( null, ModelProblem.Severity.ERROR, null, - result.getEffectiveModel(), -1, -1, iarte ) ); - } + initProject( project, projectIndex, result ); + } + catch ( InvalidArtifactRTException iarte ) + { + result.getProblems().add( new DefaultModelProblem( null, ModelProblem.Severity.ERROR, + null, result.getEffectiveModel(), -1, -1, iarte ) ); + } - List modules = new ArrayList<>(); - noErrors = build( results, modules, projectIndex, interimResult.modules ) && noErrors; + List results = build( projectIndex, interimResult.modules ); - projects.addAll( modules ); - projects.add( project ); + project.setExecutionRoot( interimResult.root ); + project.setCollectedProjects( results.stream().map( ProjectBuildingResult::getProject ) + .collect( Collectors.toList() ) ); + DependencyResolutionResult resolutionResult = null; + if ( request.isResolveDependencies() ) + { + resolutionResult = resolveDependencies( project ); + } - project.setExecutionRoot( interimResult.root ); - project.setCollectedProjects( modules ); - DependencyResolutionResult resolutionResult = null; - if ( request.isResolveDependencies() ) - { - resolutionResult = resolveDependencies( project ); - } + results.add( new DefaultProjectBuildingResult( + project, result.getProblems(), resolutionResult ) ); - results.add( new DefaultProjectBuildingResult( project, result.getProblems(), resolutionResult ) ); + return results; + } + catch ( ModelBuildingException e ) + { + DefaultProjectBuildingResult result; + if ( project == null || interimResult.result.getEffectiveModel() == null ) + { + result = new DefaultProjectBuildingResult( + e.getModelId(), interimResult.pomFile, e.getProblems() ); } - catch ( ModelBuildingException e ) + else { - DefaultProjectBuildingResult result = 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 ); - - noErrors = false; + project.setModel( interimResult.result.getEffectiveModel() ); + result = new DefaultProjectBuildingResult( project, e.getProblems(), null ); } + return Collections.singletonList( result ); } - - return noErrors; } @SuppressWarnings( "checkstyle:methodlength" ) @@ -844,7 +866,7 @@ private void initParent( MavenProject project, Map projects, // org.apache.maven.its.mng4834:parent:0.1 String parentModelId = result.getModelIds().get( 1 ); File parentPomFile = result.getRawModel( parentModelId ).getPomFile(); - MavenProject parent = projects.get( parentPomFile ); + MavenProject parent = parentPomFile != null ? projects.get( parentPomFile ) : null; if ( parent == null ) { // diff --git a/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java b/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java index 148d21586231..e3243c05d9a1 100644 --- a/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java +++ b/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java @@ -72,6 +72,22 @@ public ProjectBuildingException( List results ) this.results = results; } + public ProjectBuildingException( Throwable cause ) + { + super( cause ); + if ( cause instanceof ProjectBuildingException ) + { + ProjectBuildingException pbe = (ProjectBuildingException) cause; + this.projectId = pbe.projectId; + this.pomFile = pbe.pomFile; + this.results = pbe.results; + } + else + { + throw new IllegalArgumentException(); + } + } + public File getPomFile() { return pomFile; diff --git a/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java b/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java index de8af7e5e6bc..89a239f4ed11 100644 --- a/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java +++ b/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java @@ -22,11 +22,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.model.Model; @@ -39,9 +39,9 @@ */ class ReactorModelPool { - private final Map> modelsByGa = new HashMap<>(); + private final Map> modelsByGa = new ConcurrentHashMap<>(); - private final Map modelsByPath = new HashMap<>(); + private final Map modelsByPath = new ConcurrentHashMap<>(); /** * Get the model by its GAV or (since 4.0.0) by its GA if there is only one. From d0bc95fd53a4fe3479f8849014cce85e5eeadbc3 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 14 Sep 2022 15:39:35 +0200 Subject: [PATCH 04/16] Lazy computation in DefaultModelBuilder --- .../maven/project/DefaultProjectBuilder.java | 49 +++-- .../ConsumerModelSourceTransformerTest.java | 4 +- .../cyclic-reference/module-a/pom.xml | 1 - .../cyclic-reference/module-b/pom.xml | 1 - .../2.0/maven-plugin-api-2.0.pom | 2 +- .../plexus-containers-1.0-alpha-16.pom | 2 +- .../plexus-containers-1.0-alpha-16.pom | 2 +- .../DefaultBuildPomXMLFilterFactory.java | 11 +- .../model/building/DefaultModelBuilder.java | 202 +++++++++++------- .../building/DefaultTransformerContext.java | 4 +- .../maven/model/building/ModelCache.java | 19 ++ .../model/building/TransformerContext.java | 8 +- .../BuildToRawPomXMLFilterFactory.java | 20 +- .../model/transform/ParentXMLFilter.java | 70 +++--- .../transform/ReactorDependencyXMLFilter.java | 59 ++--- .../transform/ConsumerPomXMLFilterTest.java | 4 +- .../model/transform/ParentXMLFilterTest.java | 6 +- .../ReactorDependencyXMLFilterTest.java | 8 +- .../internal/DefaultModelCache.java | 31 ++- 19 files changed, 306 insertions(+), 197 deletions(-) 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 eb0dd60ca8ac..d08f7e434be5 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 @@ -41,6 +41,7 @@ import java.util.concurrent.ForkJoinTask; import java.util.stream.Collectors; +import org.apache.maven.ProjectCycleException; import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.InvalidArtifactRTException; @@ -77,6 +78,7 @@ import org.apache.maven.repository.internal.ModelCacheFactory; import org.codehaus.plexus.util.Os; import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.dag.CycleDetectedException; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RequestTrace; @@ -361,30 +363,36 @@ ProjectBuildingResult build( Artifact artifact, boolean allowStubModel ) List build( List pomFiles, boolean recursive ) throws ProjectBuildingException { - try - { - List results = - forkJoinPool.submit( () -> doBuild( pomFiles, recursive ) ).join(); + ForkJoinTask> task = forkJoinPool.submit( + () -> doBuild( pomFiles, recursive ) ); - if ( results.stream().flatMap( r -> r.getProblems().stream() ) - .anyMatch( p -> p.getSeverity() != ModelProblem.Severity.WARNING ) ) - { - throw new ProjectBuildingException( results ); - } - - return results; + // ForkJoinTask.getException rewraps the exception in a weird way + // which cause an additional layer of exception, so try to unwrap it + task.quietlyJoin(); + if ( task.isCompletedAbnormally() ) + { + Throwable e = task.getException(); + Throwable c = e.getCause(); + uncheckedThrow( c != null && c.getClass() == e.getClass() ? c : e ); } - catch ( Exception e ) + + List results = task.getRawResult(); + if ( results.stream().flatMap( r -> r.getProblems().stream() ) + .anyMatch( p -> p.getSeverity() != ModelProblem.Severity.WARNING ) ) { - for ( Throwable t = e; t != null; t = t.getCause() ) + ModelProblem cycle = results.stream().flatMap( r -> r.getProblems().stream() ) + .filter( p -> p.getException() instanceof CycleDetectedException ) + .findAny().orElse( null ); + if ( cycle != null ) { - if ( t instanceof ProjectBuildingException ) - { - throw ( ProjectBuildingException ) t; - } + throw new RuntimeException( new ProjectCycleException( + "The projects in the reactor contain a cyclic reference: " + cycle.getMessage(), + ( CycleDetectedException ) cycle.getException() ) ); } - throw new RuntimeException( e ); + throw new ProjectBuildingException( results ); } + + return results; } List doBuild( List pomFiles, boolean recursive ) @@ -1052,4 +1060,9 @@ private static String inheritedVersion( final ModelBuildingResult result, final return version; } + static void uncheckedThrow( Throwable t ) throws T + { + throw (T) t; // rely on vacuous cast + } + } diff --git a/maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java b/maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java index 50506239fb99..15a3ad5cc5af 100644 --- a/maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java +++ b/maven-core/src/test/java/org/apache/maven/internal/aether/ConsumerModelSourceTransformerTest.java @@ -55,14 +55,14 @@ public String getUserProperty( String key ) } @Override - public Model getRawModel( String groupId, String artifactId ) + public Model getRawModel( Path from, String groupId, String artifactId ) throws IllegalStateException { return null; } @Override - public Model getRawModel( Path p ) + public Model getRawModel( Path from, Path p ) { return null; } diff --git a/maven-core/src/test/projects/default-maven/cyclic-reference/module-a/pom.xml b/maven-core/src/test/projects/default-maven/cyclic-reference/module-a/pom.xml index ec8d8055c3be..e100aa605c96 100644 --- a/maven-core/src/test/projects/default-maven/cyclic-reference/module-a/pom.xml +++ b/maven-core/src/test/projects/default-maven/cyclic-reference/module-a/pom.xml @@ -13,7 +13,6 @@ cyclic-reference module-b - 1.0-SNAPSHOT diff --git a/maven-core/src/test/projects/default-maven/cyclic-reference/module-b/pom.xml b/maven-core/src/test/projects/default-maven/cyclic-reference/module-b/pom.xml index 119fb5f16e10..52b5f74c6326 100644 --- a/maven-core/src/test/projects/default-maven/cyclic-reference/module-b/pom.xml +++ b/maven-core/src/test/projects/default-maven/cyclic-reference/module-b/pom.xml @@ -13,7 +13,6 @@ cyclic-reference module-a - 1.0-SNAPSHOT diff --git a/maven-core/src/test/remote-repo/org/apache/maven/maven-plugin-api/2.0/maven-plugin-api-2.0.pom b/maven-core/src/test/remote-repo/org/apache/maven/maven-plugin-api/2.0/maven-plugin-api-2.0.pom index 5db5973762fc..c1d5cc0b836a 100644 --- a/maven-core/src/test/remote-repo/org/apache/maven/maven-plugin-api/2.0/maven-plugin-api-2.0.pom +++ b/maven-core/src/test/remote-repo/org/apache/maven/maven-plugin-api/2.0/maven-plugin-api-2.0.pom @@ -13,7 +13,7 @@ junit junit - + 4.13.1 test diff --git a/maven-core/src/test/resources/apiv4-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom b/maven-core/src/test/resources/apiv4-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom index 1e8675857945..bdcd0e3874fc 100644 --- a/maven-core/src/test/resources/apiv4-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom +++ b/maven-core/src/test/resources/apiv4-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom @@ -23,7 +23,7 @@ junit junit - + 4.13.1 compile diff --git a/maven-core/src/test/resources/org/apache/maven/extension/test-extension-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom b/maven-core/src/test/resources/org/apache/maven/extension/test-extension-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom index 1e8675857945..bdcd0e3874fc 100644 --- a/maven-core/src/test/resources/org/apache/maven/extension/test-extension-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom +++ b/maven-core/src/test/resources/org/apache/maven/extension/test-extension-repo/org/codehaus/plexus/plexus-containers/1.0-alpha-16/plexus-containers-1.0-alpha-16.pom @@ -23,7 +23,7 @@ junit junit - + 4.13.1 compile 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 e4154612817b..b5d4c7c9dc90 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 @@ -20,10 +20,7 @@ */ -import java.nio.file.Path; import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Function; import org.apache.maven.model.Model; import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory; @@ -52,16 +49,16 @@ public DefaultBuildPomXMLFilterFactory( TransformerContext context, } @Override - protected Function> getRelativePathMapper() + protected RelativePathMapper getRelativePathMapper() { - return p -> Optional.ofNullable( context.getRawModel( p ) ) + return ( from, p ) -> Optional.ofNullable( context.getRawModel( from, p ) ) .map( DefaultBuildPomXMLFilterFactory::toRelativeProject ); } @Override - protected BiFunction getDependencyKeyToVersionMapper() + protected DependencyKeyToVersionMapper getDependencyKeyToVersionMapper() { - return ( g, a ) -> Optional.ofNullable( context.getRawModel( g, a ) ) + return ( from, g, a ) -> Optional.ofNullable( context.getRawModel( from, g, a ) ) .map( DefaultBuildPomXMLFilterFactory::toVersion ) .orElse( null ); } 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 3e21fc72e735..ed87b225da7d 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 @@ -29,7 +29,6 @@ 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; @@ -37,9 +36,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinTask; +import java.util.function.Function; +import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Named; @@ -95,6 +99,8 @@ import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.interpolation.MapBasedValueSource; import org.codehaus.plexus.interpolation.StringSearchInterpolator; +import org.codehaus.plexus.util.dag.CycleDetectedException; +import org.codehaus.plexus.util.dag.DAG; import org.eclipse.sisu.Nullable; /** @@ -525,6 +531,10 @@ private Model readEffectiveModel( final ModelBuildingRequest request, final Defa { Model inputModel = readRawModel( request, problems ); + if ( problems.hasFatalErrors() ) + { + throw problems.newModelBuildingException(); + } problems.setRootModel( inputModel ); @@ -810,13 +820,8 @@ private Model readFileModel( ModelBuildingRequest request, throws ModelBuildingException { ModelSource modelSource = request.getModelSource(); - org.apache.maven.api.model.Model model = fromCache( request.getModelCache(), modelSource, ModelCacheTag.FILE ); - if ( model == null ) - { - model = doReadFileModel( modelSource, request, problems ); - - intoCache( request.getModelCache(), modelSource, ModelCacheTag.FILE, model ); - } + org.apache.maven.api.model.Model model = cache( request.getModelCache(), modelSource, ModelCacheTag.FILE, + () -> doReadFileModel( modelSource, request, problems ) ); if ( modelSource instanceof FileModelSource ) { @@ -824,7 +829,7 @@ private Model readFileModel( ModelBuildingRequest request, { DefaultTransformerContextBuilder contextBuilder = (DefaultTransformerContextBuilder) request.getTransformerContextBuilder(); - contextBuilder.putSource( getGroupId( model ), model.getArtifactId(), modelSource ); + contextBuilder.putSource( getGroupId( model ), model.getArtifactId(), ( FileModelSource ) modelSource ); } } @@ -952,12 +957,16 @@ private Model readRawModel( ModelBuildingRequest request, DefaultModelProblemCol { ModelSource modelSource = request.getModelSource(); - ModelData cachedData = fromCache( request.getModelCache(), modelSource, ModelCacheTag.RAW ); - if ( cachedData != null ) - { - return cachedData.getModel(); - } + ModelData modelData = cache( request.getModelCache(), modelSource, ModelCacheTag.RAW, + () -> doReadRawModel( modelSource, request, problems ) ); + return modelData.getModel(); + } + + private ModelData doReadRawModel( ModelSource modelSource, ModelBuildingRequest request, + DefaultModelProblemCollector problems ) + throws ModelBuildingException + { Model rawModel; if ( Features.buildConsumer( request.getUserProperties() ).isActive() && modelSource instanceof FileModelSource ) @@ -974,10 +983,12 @@ private Model readRawModel( ModelBuildingRequest request, DefaultModelProblemCol try { // must implement TransformContext, but should use request to access system properties/modelcache - org.apache.maven.api.model.Model transformedFileModel = modelProcessor.read( pomFile, - Collections.singletonMap( ModelReader.TRANSFORMER_CONTEXT, context ) ); + Map options = new HashMap<>(); + options.put( ModelReader.TRANSFORMER_CONTEXT, context ); + org.apache.maven.api.model.Model transformedFileModel = modelProcessor.read( pomFile, options ); // rawModel with locationTrackers, required for proper feedback during validations + // TODO: we should be able to do a single pass with location tracking // Apply enriched data rawModel = new Model( modelMerger.merge( rawModel.getDelegate(), @@ -1008,10 +1019,7 @@ else if ( request.getFileModel() == null ) String artifactId = rawModel.getArtifactId(); String version = getVersion( rawModel ); - ModelData modelData = new ModelData( modelSource, rawModel, groupId, artifactId, version ); - intoCache( request.getModelCache(), modelSource, ModelCacheTag.RAW, modelData ); - - return rawModel; + return new ModelData( modelSource, rawModel, groupId, artifactId, version ); } private String getGroupId( Model model ) @@ -1607,18 +1615,11 @@ private DependencyManagement loadDependencyManagement( Model model, ModelBuildin } org.apache.maven.api.model.DependencyManagement importMgmt = - fromCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT ); - if ( importMgmt == null ) - { - DependencyManagement importMgmtV3 = doLoadDependencyManagement( model, request, problems, dependency, - groupId, artifactId, version, importIds ); - if ( importMgmtV3 != null ) - { - importMgmt = importMgmtV3.getDelegate(); - intoCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, - importMgmt ); - } - } + cache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, + () -> Optional.ofNullable( doLoadDependencyManagement( + model, request, problems, dependency, groupId, artifactId, version, importIds ) ) + .map( DependencyManagement::getDelegate ) + .orElse( null ) ); return importMgmt != null ? new DependencyManagement( importMgmt ) : null; } @@ -1717,45 +1718,64 @@ private DependencyManagement doLoadDependencyManagement( Model model, ModelBuild return importMgmt; } - private void intoCache( ModelCache modelCache, String groupId, String artifactId, String version, - ModelCacheTag tag, T data ) + private static T cache( ModelCache cache, String groupId, String artifactId, String version, + ModelCacheTag tag, Callable supplier ) { - if ( modelCache != null ) - { - modelCache.put( groupId, artifactId, version, tag, data ); - } + return doWithCache( cache, supplier, s -> cache.computeIfAbsent( groupId, artifactId, version, tag, s ) ); } - private void intoCache( ModelCache modelCache, Source source, ModelCacheTag tag, T data ) + private static T cache( ModelCache cache, Source source, + ModelCacheTag tag, Callable supplier ) { - if ( modelCache != null ) - { - modelCache.put( source, tag, data ); - } + return doWithCache( cache, supplier, s -> cache.computeIfAbsent( source, tag, s ) ); } - private static T fromCache( ModelCache modelCache, String groupId, String artifactId, String version, - ModelCacheTag tag ) + private static T doWithCache( ModelCache cache, Callable supplier, + Function>, T> asyncSupplierConsumer ) { - if ( modelCache != null ) + if ( cache != null ) + { + return asyncSupplierConsumer.apply( () -> + { + ForkJoinTask task = ForkJoinTask.adapt( supplier ); + task.fork(); + return () -> + { + task.quietlyJoin(); + if ( task.isCompletedAbnormally() ) + { + Throwable e = task.getException(); + while ( e instanceof RuntimeException && e.getCause() != null ) + { + e = e.getCause(); + } + uncheckedThrow( e ); + } + return task.getRawResult(); + }; + } ); + } + else { - return modelCache.get( groupId, artifactId, version, tag ); + try + { + return supplier.call(); + } + catch ( Exception e ) + { + uncheckedThrow( e ); + return null; + } } - return null; } - private static T fromCache( ModelCache modelCache, Source source, ModelCacheTag tag ) + static void uncheckedThrow( Throwable t ) throws T { - if ( modelCache != null ) - { - return modelCache.get( source, tag ); - } - return null; + throw (T) t; // rely on vacuous cast } private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems, ModelBuildingEventCatapult catapult ) - throws ModelBuildingException { ModelBuildingListener listener = request.getModelBuildingListener(); @@ -1814,15 +1834,13 @@ private class DefaultTransformerContextBuilder implements TransformerContextBuil { private final DefaultTransformerContext context = new DefaultTransformerContext(); - private final Map> mappedSources + private final Map> mappedSources = new ConcurrentHashMap<>( 64 ); + private final DAG dag = new DAG(); + /** * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt - * - * @param request - * @param collector - * @return */ @Override public TransformerContext initialize( ModelBuildingRequest request, ModelProblemCollector collector ) @@ -1839,37 +1857,38 @@ public String getUserProperty( String key ) } @Override - public Model getRawModel( String gId, String aId ) + public Model getRawModel( Path from, String gId, String aId ) { return context.modelByGA.computeIfAbsent( new DefaultTransformerContext.GAKey( gId, aId ), k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> findRawModel( gId, aId ) ); + .computeIfAbsent( () -> findRawModel( from, gId, aId ) ); } @Override - public Model getRawModel( Path path ) + public Model getRawModel( Path from, Path path ) { return context.modelByPath.computeIfAbsent( path, k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> findRawModel( path ) ); + .computeIfAbsent( () -> findRawModel( from, path ) ); } - private Model findRawModel( String groupId, String artifactId ) + private Model findRawModel( Path from, String groupId, String artifactId ) { - Source source = getSource( groupId, artifactId ); + FileModelSource source = getSource( groupId, artifactId ); if ( source != null ) { + if ( !addEdge( from, source.getFile().toPath(), problems ) ) + { + return null; + } try { ModelBuildingRequest gaBuildingRequest = new DefaultModelBuildingRequest( request ) - .setModelSource( (ModelSource) source ); + .setModelSource( source ); Model model = readRawModel( gaBuildingRequest, problems ); - if ( source instanceof FileModelSource ) - { - Path path = ( ( FileModelSource ) source ).getFile().toPath(); - context.modelByPath.computeIfAbsent( path, k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> model ); - } + Path path = source.getFile().toPath(); + context.modelByPath.computeIfAbsent( path, k -> new DefaultTransformerContext.Holder() ) + .computeIfAbsent( () -> model ); return model; } catch ( ModelBuildingException e ) @@ -1880,13 +1899,18 @@ private Model findRawModel( String groupId, String artifactId ) return null; } - private Model findRawModel( Path p ) + private Model findRawModel( Path from, Path p ) { if ( !Files.isRegularFile( p ) ) { throw new IllegalArgumentException( "Not a regular file: " + p ); } + if ( !addEdge( from, p, problems ) ) + { + return null; + } + DefaultModelBuildingRequest req = new DefaultModelBuildingRequest( request ) .setPomFile( p.toFile() ) .setModelSource( new FileModelSource( p.toFile() ) ); @@ -1909,15 +1933,38 @@ private Model findRawModel( Path p ) }; } + private boolean addEdge( Path from, Path p, DefaultModelProblemCollector problems ) + { + try + { + synchronized ( dag ) + { + dag.addEdge( from.toString(), p.toString() ); + } + return true; + } + catch ( CycleDetectedException e ) + { + problems.add( new DefaultModelProblem( + "Cycle detected between models at " + from + " and " + p, + Severity.FATAL, + null, null, 0, 0, null, + e + ) ); + return false; + } + } + @Override public TransformerContext build() { return context; } - public Source getSource( String groupId, String artifactId ) + public FileModelSource getSource( String groupId, String artifactId ) { - Set sources = mappedSources.get( new DefaultTransformerContext.GAKey( groupId, artifactId ) ); + Set sources = mappedSources.get( + new DefaultTransformerContext.GAKey( groupId, artifactId ) ); if ( sources == null ) { return null; @@ -1930,11 +1977,10 @@ public Source getSource( String groupId, String artifactId ) } ).orElse( null ); } - public void putSource( String groupId, String artifactId, Source source ) + public void putSource( String groupId, String artifactId, FileModelSource 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/DefaultTransformerContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContext.java index 3c70b3c9af03..459e16c4e2b5 100644 --- 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 @@ -102,13 +102,13 @@ public String getUserProperty( String key ) } @Override - public Model getRawModel( Path p ) + public Model getRawModel( Path from, Path p ) { return Holder.deref( modelByPath.get( p ) ); } @Override - public Model getRawModel( String groupId, String artifactId ) + public Model getRawModel( Path from, String groupId, String artifactId ) { return Holder.deref( modelByGA.get( new GAKey( groupId, artifactId ) ) ); } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java index 25fb268ae6ed..5d01cb51d2fc 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelCache.java @@ -19,6 +19,8 @@ * under the License. */ +import java.util.function.Supplier; + import org.apache.maven.building.Source; /** @@ -138,4 +140,21 @@ default T get( String groupId, String artifactId, String version, ModelCache return ( obj != null ) ? tag.fromCache( tag.getType().cast( obj ) ) : null; } + default T computeIfAbsent( String groupId, String artifactId, String version, + ModelCacheTag tag, Supplier> data ) + { + Object obj = computeIfAbsent( groupId, artifactId, version, tag.getName(), ( Supplier ) data ); + return ( obj != null ) ? tag.fromCache( tag.getType().cast( obj ) ) : null; + } + + default T computeIfAbsent( Source path, ModelCacheTag tag, Supplier> data ) + { + Object obj = computeIfAbsent( path, tag.getName(), ( Supplier ) data ); + return ( obj != null ) ? tag.fromCache( tag.getType().cast( obj ) ) : null; + } + + Object computeIfAbsent( String groupId, String artifactId, String version, String tag, Supplier> data ); + + Object computeIfAbsent( Source path, String tag, Supplier> data ); + } 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 e041af4912db..a57d75950db5 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,18 +47,20 @@ public interface TransformerContext /** * Get the model based on the path, will be used to resolve the parent based on relativePath * + * @param from the requiring model * @param pomFile the path to the pomFile * @return the model, otherwise {@code null} */ - Model getRawModel( Path pomFile ); + Model getRawModel( Path from, Path pomFile ); /** * Get the model from the reactor based on the groupId and artifactId, will be used for reactor dependencies * - * @param groupId the groupId + * @param from the requiring model + * @param groupId the groupId * @param artifactId the artifactId * @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 ); + Model getRawModel( Path from, String groupId, String artifactId ); } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java index dba9026b12f3..ef7b6534f69a 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/BuildToRawPomXMLFilterFactory.java @@ -21,8 +21,6 @@ import java.nio.file.Path; import java.util.Optional; -import java.util.function.BiFunction; -import java.util.function.Function; import org.codehaus.plexus.util.xml.pull.XmlPullParser; @@ -36,6 +34,16 @@ public class BuildToRawPomXMLFilterFactory { private final boolean consume; + public interface RelativePathMapper + { + Optional apply( Path from, Path path ); + } + + public interface DependencyKeyToVersionMapper + { + String apply( Path from, String g, String a ); + } + public BuildToRawPomXMLFilterFactory() { this( false ); @@ -59,12 +67,12 @@ public final XmlPullParser get( XmlPullParser orgParser, Path projectFile ) if ( getDependencyKeyToVersionMapper() != null ) { - parser = new ReactorDependencyXMLFilter( parser, getDependencyKeyToVersionMapper() ); + parser = new ReactorDependencyXMLFilter( parser, getDependencyKeyToVersionMapper(), projectFile ); } if ( getRelativePathMapper() != null ) { - parser = new ParentXMLFilter( parser, getRelativePathMapper(), projectFile.getParent() ); + parser = new ParentXMLFilter( parser, getRelativePathMapper(), projectFile ); } CiFriendlyXMLFilter ciFriendlyFilter = new CiFriendlyXMLFilter( parser, consume ); @@ -79,12 +87,12 @@ public final XmlPullParser get( XmlPullParser orgParser, Path projectFile ) /** * @return the mapper or {@code null} if relativePaths don't need to be mapped */ - protected Function> getRelativePathMapper() + protected RelativePathMapper getRelativePathMapper() { return null; } - protected BiFunction getDependencyKeyToVersionMapper() + protected DependencyKeyToVersionMapper getDependencyKeyToVersionMapper() { return null; } diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java index 37ac28b62404..6ba754a2c6f8 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ParentXMLFilter.java @@ -25,10 +25,10 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.function.Function; import org.apache.maven.model.transform.pull.NodeBufferingParser; import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory.RelativePathMapper; /** *

@@ -46,19 +46,20 @@ class ParentXMLFilter extends NodeBufferingParser { - private final Function> relativePathMapper; + private final RelativePathMapper relativePathMapper; - private final Path projectPath; + private final Path projectFile; /** * @param relativePathMapper */ ParentXMLFilter( XmlPullParser parser, - Function> relativePathMapper, Path projectPath ) + RelativePathMapper relativePathMapper, + Path projectFile ) { super( parser, "parent" ); this.relativePathMapper = relativePathMapper; - this.projectPath = projectPath; + this.projectFile = projectFile; } protected void process( List buffer ) @@ -108,39 +109,34 @@ else if ( "version".equals( tagName ) ) } else if ( event.event == END_TAG && "parent".equals( event.name ) ) { - Optional resolvedParent; if ( !hasVersion && ( !hasRelativePath || relativePath != null ) ) { Path relPath = Paths.get( Objects.toString( relativePath, "../pom.xml" ) ); - resolvedParent = resolveRelativePath( relPath, groupId, artifactId ); - } - else - { - resolvedParent = Optional.empty(); - } - if ( !hasVersion && resolvedParent.isPresent() ) - { - int pos = buffer.get( i - 1 ).event == TEXT ? i - 1 : i; - Event e = new Event(); - e.event = TEXT; - e.text = whitespaceAfterParentStart; - buffer.add( pos++, e ); - e = new Event(); - e.event = START_TAG; - e.namespace = buffer.get( 0 ).namespace; - e.prefix = buffer.get( 0 ).prefix; - e.name = "version"; - buffer.add( pos++, e ); - e = new Event(); - e.event = TEXT; - e.text = resolvedParent.get().getVersion(); - buffer.add( pos++, e ); - e = new Event(); - e.event = END_TAG; - e.name = "version"; - e.namespace = buffer.get( 0 ).namespace; - e.prefix = buffer.get( 0 ).prefix; - buffer.add( pos++, e ); + Optional resolvedParent = resolveRelativePath( relPath, groupId, artifactId ); + if ( resolvedParent.isPresent() ) + { + int pos = buffer.get( i - 1 ).event == TEXT ? i - 1 : i; + Event e = new Event(); + e.event = TEXT; + e.text = whitespaceAfterParentStart; + buffer.add( pos++, e ); + e = new Event(); + e.event = START_TAG; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + e.name = "version"; + buffer.add( pos++, e ); + e = new Event(); + e.event = TEXT; + e.text = resolvedParent.get().getVersion(); + buffer.add( pos++, e ); + e = new Event(); + e.event = END_TAG; + e.name = "version"; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + buffer.add( pos++, e ); + } } break; } @@ -151,13 +147,13 @@ else if ( event.event == END_TAG && "parent".equals( event.name ) ) protected Optional resolveRelativePath( Path relativePath, String groupId, String artifactId ) { - Path pomPath = projectPath.resolve( relativePath ); + Path pomPath = projectFile.resolveSibling( relativePath ); if ( Files.isDirectory( pomPath ) ) { pomPath = pomPath.resolve( "pom.xml" ); } - Optional mappedProject = relativePathMapper.apply( pomPath.normalize() ); + Optional mappedProject = relativePathMapper.apply( projectFile, pomPath.normalize() ); if ( mappedProject.isPresent() ) { diff --git a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java index 7f9dd3ba4f41..1d7141e16b0a 100644 --- a/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java +++ b/maven-model-transform/src/main/java/org/apache/maven/model/transform/ReactorDependencyXMLFilter.java @@ -19,11 +19,12 @@ * under the License. */ +import java.nio.file.Path; import java.util.List; -import java.util.function.BiFunction; import org.apache.maven.model.transform.pull.NodeBufferingParser; import org.codehaus.plexus.util.xml.pull.XmlPullParser; +import org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory.DependencyKeyToVersionMapper; /** * Will apply the version if the dependency is part of the reactor @@ -34,13 +35,16 @@ */ public class ReactorDependencyXMLFilter extends NodeBufferingParser { - private final BiFunction reactorVersionMapper; + private final DependencyKeyToVersionMapper reactorVersionMapper; + private final Path projectFile; public ReactorDependencyXMLFilter( XmlPullParser xmlPullParser, - BiFunction reactorVersionMapper ) + DependencyKeyToVersionMapper reactorVersionMapper, + Path projectFile ) { super( xmlPullParser, "dependency" ); this.reactorVersionMapper = reactorVersionMapper; + this.projectFile = projectFile; } protected void process( List buffer ) @@ -79,30 +83,33 @@ else if ( "artifactId".equals( tagName ) ) } else if ( event.event == END_TAG && "dependency".equals( event.name ) ) { - String version = reactorVersionMapper.apply( groupId, artifactId ); - if ( !hasVersion && version != null ) + if ( !hasVersion ) { - int pos = buffer.get( i - 1 ).event == TEXT ? i - 1 : i; - Event e = new Event(); - e.event = TEXT; - e.text = dependencyWhitespace; - buffer.add( pos++, e ); - e = new Event(); - e.event = START_TAG; - e.namespace = buffer.get( 0 ).namespace; - e.prefix = buffer.get( 0 ).prefix; - e.name = "version"; - buffer.add( pos++, e ); - e = new Event(); - e.event = TEXT; - e.text = version; - buffer.add( pos++, e ); - e = new Event(); - e.event = END_TAG; - e.name = "version"; - e.namespace = buffer.get( 0 ).namespace; - e.prefix = buffer.get( 0 ).prefix; - buffer.add( pos++, e ); + String version = reactorVersionMapper.apply( projectFile, groupId, artifactId ); + if ( version != null ) + { + int pos = buffer.get( i - 1 ).event == TEXT ? i - 1 : i; + Event e = new Event(); + e.event = TEXT; + e.text = dependencyWhitespace; + buffer.add( pos++, e ); + e = new Event(); + e.event = START_TAG; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + e.name = "version"; + buffer.add( pos++, e ); + e = new Event(); + e.event = TEXT; + e.text = version; + buffer.add( pos++, e ); + e = new Event(); + e.event = END_TAG; + e.name = "version"; + e.namespace = buffer.get( 0 ).namespace; + e.prefix = buffer.get( 0 ).prefix; + buffer.add( pos++, e ); + } } break; } diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java index ee77d1244e54..6dc5b9adbe2e 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ConsumerPomXMLFilterTest.java @@ -38,13 +38,13 @@ protected XmlPullParser getFilter( XmlPullParser orgParser ) final BuildToRawPomXMLFilterFactory buildPomXMLFilterFactory = new BuildToRawPomXMLFilterFactory( true ) { @Override - protected Function> getRelativePathMapper() + protected RelativePathMapper getRelativePathMapper() { return null; } @Override - protected BiFunction getDependencyKeyToVersionMapper() + protected DependencyKeyToVersionMapper getDependencyKeyToVersionMapper() { return null; } diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java index 85714934dd3c..adb42c2a4a05 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ParentXMLFilterTest.java @@ -50,11 +50,11 @@ protected XmlPullParser getFilter( XmlPullParser parser ) protected XmlPullParser createFilter( XmlPullParser parser ) { return createFilter( parser, - x -> Optional.of(new RelativeProject("GROUPID", "ARTIFACTID", "1.0.0")), + (from, x) -> Optional.of(new RelativeProject("GROUPID", "ARTIFACTID", "1.0.0")), Paths.get( "pom.xml").toAbsolutePath() ); } - protected XmlPullParser createFilter( XmlPullParser parser, Function> pathMapper, Path projectPath ) { + protected XmlPullParser createFilter( XmlPullParser parser, BuildToRawPomXMLFilterFactory.RelativePathMapper pathMapper, Path projectPath ) { return new ParentXMLFilter( new FastForwardFilter( parser ), pathMapper, projectPath ); } @@ -230,7 +230,7 @@ public void testNoVersion() public void testInvalidRelativePath() throws Exception { - filterCreator = parser -> createFilter(parser, x -> Optional.ofNullable( null ), Paths.get( "pom.xml").toAbsolutePath() ); + filterCreator = parser -> createFilter(parser, (from, x) -> Optional.ofNullable( null ), Paths.get( "pom.xml").toAbsolutePath() ); String input = "" + "GROUPID" diff --git a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java index 0304ea606a73..a462b7398d2f 100644 --- a/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java +++ b/maven-model-transform/src/test/java/org/apache/maven/model/transform/ReactorDependencyXMLFilterTest.java @@ -19,6 +19,7 @@ * under the License. */ +import java.nio.file.Paths; import java.util.function.BiFunction; import org.codehaus.plexus.util.xml.pull.XmlPullParser; @@ -30,7 +31,7 @@ public class ReactorDependencyXMLFilterTest extends AbstractXMLFilterTests { - private BiFunction reactorVersionMapper; + private BuildToRawPomXMLFilterFactory.DependencyKeyToVersionMapper reactorVersionMapper; @BeforeEach protected void reset() { @@ -41,7 +42,8 @@ protected void reset() { protected ReactorDependencyXMLFilter getFilter(XmlPullParser parser) { return new ReactorDependencyXMLFilter( parser, - reactorVersionMapper != null ? reactorVersionMapper : (g, a) -> "1.0.0" ); + reactorVersionMapper != null ? reactorVersionMapper : (from, g, a) -> "1.0.0", + Paths.get( "theproject/pom.xml" ) ); } @Test @@ -64,7 +66,7 @@ public void testDefaultDependency() public void testManagedDependency() throws Exception { - reactorVersionMapper = (g, a) -> null; + reactorVersionMapper = (from, g, a) -> null; String input = "" + "GROUPID" diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java index 339bc08b8bf0..62545f6595d0 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import org.apache.maven.building.Source; import org.apache.maven.model.building.ModelCache; @@ -38,11 +39,11 @@ public class DefaultModelCache private static final String KEY = DefaultModelCache.class.getName(); - private final Map cache; + private final Map> cache; public static ModelCache newInstance( RepositorySystemSession session ) { - Map cache; + Map> cache; if ( session.getCache() == null ) { cache = new ConcurrentHashMap<>(); @@ -59,7 +60,7 @@ public static ModelCache newInstance( RepositorySystemSession session ) return new DefaultModelCache( cache ); } - private DefaultModelCache( Map cache ) + private DefaultModelCache( Map> cache ) { this.cache = cache; } @@ -86,12 +87,32 @@ public void put( String groupId, String artifactId, String version, String tag, protected Object get( Object key ) { - return cache.get( key ); + Supplier s = cache.get( key ); + return s != null ? s.get() : null; } protected void put( Object key, Object data ) { - cache.put( key, data ); + cache.put( key, () -> data ); + } + + @Override + public Object computeIfAbsent( String groupId, String artifactId, String version, String tag, + Supplier> data ) + { + return computeIfAbsent( new GavCacheKey( groupId, artifactId, version, tag ), data ); + } + + @Override + public Object computeIfAbsent( Source path, String tag, Supplier> data ) + { + return computeIfAbsent( new SourceCacheKey( path, tag ), data ); + } + + protected Object computeIfAbsent( Object key, Supplier> data ) + { + Supplier s = cache.computeIfAbsent( key, k -> data.get() ); + return s != null ? s.get() : null; } static class GavCacheKey From 80314df01470015095e2efef9116688662e64827 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 15 Sep 2022 00:41:31 +0200 Subject: [PATCH 05/16] Extract DefaultTransformerContextBuilder --- .../model/building/DefaultModelBuilder.java | 174 +-------------- .../DefaultTransformerContextBuilder.java | 199 ++++++++++++++++++ 2 files changed, 202 insertions(+), 171 deletions(-) create mode 100644 maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java 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 ed87b225da7d..867b3177ce1e 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,12 +25,9 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Field; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; @@ -38,9 +35,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Properties; -import java.util.Set; import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ForkJoinTask; import java.util.function.Function; import java.util.function.Supplier; @@ -99,8 +94,6 @@ import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.interpolation.MapBasedValueSource; import org.codehaus.plexus.interpolation.StringSearchInterpolator; -import org.codehaus.plexus.util.dag.CycleDetectedException; -import org.codehaus.plexus.util.dag.DAG; import org.eclipse.sisu.Nullable; /** @@ -435,7 +428,7 @@ public DefaultModelBuilder setProfileActivationFilePathInterpolator( @Override public DefaultTransformerContextBuilder newTransformerContextBuilder() { - return new DefaultTransformerContextBuilder(); + return new DefaultTransformerContextBuilder( this ); } @Override @@ -952,7 +945,7 @@ private org.apache.maven.api.model.Model doReadFileModel( ModelSource modelSourc return model; } - private Model readRawModel( ModelBuildingRequest request, DefaultModelProblemCollector problems ) + Model readRawModel( ModelBuildingRequest request, DefaultModelProblemCollector problems ) throws ModelBuildingException { ModelSource modelSource = request.getModelSource(); @@ -1022,7 +1015,7 @@ else if ( request.getFileModel() == null ) return new ModelData( modelSource, rawModel, groupId, artifactId, version ); } - private String getGroupId( Model model ) + String getGroupId( Model model ) { return getGroupId( model.getDelegate() ); } @@ -1822,165 +1815,4 @@ protected boolean hasFatalErrors( ModelProblemCollectorExt problems ) } } - /** - * 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 ); - - private final DAG dag = new DAG(); - - /** - * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt - */ - @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( Path from, String gId, String aId ) - { - return context.modelByGA.computeIfAbsent( new DefaultTransformerContext.GAKey( gId, aId ), - k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> findRawModel( from, gId, aId ) ); - } - - @Override - public Model getRawModel( Path from, Path path ) - { - return context.modelByPath.computeIfAbsent( path, - k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> findRawModel( from, path ) ); - } - - private Model findRawModel( Path from, String groupId, String artifactId ) - { - FileModelSource source = getSource( groupId, artifactId ); - if ( source != null ) - { - if ( !addEdge( from, source.getFile().toPath(), problems ) ) - { - return null; - } - try - { - ModelBuildingRequest gaBuildingRequest = new DefaultModelBuildingRequest( request ) - .setModelSource( source ); - Model model = readRawModel( gaBuildingRequest, problems ); - Path path = source.getFile().toPath(); - context.modelByPath.computeIfAbsent( path, k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> model ); - return model; - } - catch ( ModelBuildingException e ) - { - // gathered with problem collector - } - } - return null; - } - - private Model findRawModel( Path from, Path p ) - { - if ( !Files.isRegularFile( p ) ) - { - throw new IllegalArgumentException( "Not a regular file: " + p ); - } - - if ( !addEdge( from, p, problems ) ) - { - return null; - } - - DefaultModelBuildingRequest req = new DefaultModelBuildingRequest( request ) - .setPomFile( p.toFile() ) - .setModelSource( new FileModelSource( p.toFile() ) ); - - try - { - Model model = readRawModel( req, problems ); - DefaultTransformerContext.GAKey key = - new DefaultTransformerContext.GAKey( getGroupId( model ), model.getArtifactId() ); - context.modelByGA.computeIfAbsent( key, k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> model ); - return model; - } - catch ( ModelBuildingException e ) - { - // gathered with problem collector - } - return null; - } - }; - } - - private boolean addEdge( Path from, Path p, DefaultModelProblemCollector problems ) - { - try - { - synchronized ( dag ) - { - dag.addEdge( from.toString(), p.toString() ); - } - return true; - } - catch ( CycleDetectedException e ) - { - problems.add( new DefaultModelProblem( - "Cycle detected between models at " + from + " and " + p, - Severity.FATAL, - null, null, 0, 0, null, - e - ) ); - return false; - } - } - - @Override - public TransformerContext build() - { - return context; - } - - public FileModelSource 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, FileModelSource 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/DefaultTransformerContextBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java new file mode 100644 index 000000000000..54a237a12425 --- /dev/null +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java @@ -0,0 +1,199 @@ +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.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.model.Model; +import org.codehaus.plexus.util.dag.CycleDetectedException; +import org.codehaus.plexus.util.dag.DAG; + +/** + * Builds up the transformer context. + * After the buildplan is ready, the build()-method returns the immutable context useful during distribution. + * It must be able to call {@link DefaultModelBuilder#readRawModel(ModelBuildingRequest, DefaultModelProblemCollector)}. + * + * @author Robert Scholte + * @since 4.0.0 + */ +class DefaultTransformerContextBuilder implements TransformerContextBuilder +{ + private final DefaultTransformerContext context = new DefaultTransformerContext(); + + private final Map> mappedSources + = new ConcurrentHashMap<>( 64 ); + + private final DAG dag = new DAG(); + private final DefaultModelBuilder defaultModelBuilder; + + DefaultTransformerContextBuilder( final DefaultModelBuilder defaultModelBuilder ) + { + this.defaultModelBuilder = defaultModelBuilder; + } + + /** + * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt + */ + @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( Path from, String gId, String aId ) + { + return context.modelByGA.computeIfAbsent( new DefaultTransformerContext.GAKey( gId, aId ), + k -> new DefaultTransformerContext.Holder() ) + .computeIfAbsent( () -> findRawModel( from, gId, aId ) ); + } + + @Override + public Model getRawModel( Path from, Path path ) + { + return context.modelByPath.computeIfAbsent( path, + k -> new DefaultTransformerContext.Holder() ) + .computeIfAbsent( () -> findRawModel( from, path ) ); + } + + private Model findRawModel( Path from, String groupId, String artifactId ) + { + FileModelSource source = getSource( groupId, artifactId ); + if ( source != null ) + { + if ( !addEdge( from, source.getFile().toPath(), problems ) ) + { + return null; + } + try + { + ModelBuildingRequest gaBuildingRequest = new DefaultModelBuildingRequest( request ) + .setModelSource( source ); + Model model = defaultModelBuilder.readRawModel( gaBuildingRequest, problems ); + Path path = source.getFile().toPath(); + context.modelByPath.computeIfAbsent( path, k -> new DefaultTransformerContext.Holder() ) + .computeIfAbsent( () -> model ); + return model; + } + catch ( ModelBuildingException e ) + { + // gathered with problem collector + } + } + return null; + } + + private Model findRawModel( Path from, Path p ) + { + if ( !Files.isRegularFile( p ) ) + { + throw new IllegalArgumentException( "Not a regular file: " + p ); + } + + if ( !addEdge( from, p, problems ) ) + { + return null; + } + + DefaultModelBuildingRequest req = new DefaultModelBuildingRequest( request ) + .setPomFile( p.toFile() ) + .setModelSource( new FileModelSource( p.toFile() ) ); + + try + { + Model model = defaultModelBuilder.readRawModel( req, problems ); + DefaultTransformerContext.GAKey key = + new DefaultTransformerContext.GAKey( defaultModelBuilder.getGroupId( model ), model.getArtifactId() ); + context.modelByGA.computeIfAbsent( key, k -> new DefaultTransformerContext.Holder() ) + .computeIfAbsent( () -> model ); + return model; + } + catch ( ModelBuildingException e ) + { + // gathered with problem collector + } + return null; + } + }; + } + + private boolean addEdge( Path from, Path p, DefaultModelProblemCollector problems ) + { + try + { + synchronized ( dag ) + { + dag.addEdge( from.toString(), p.toString() ); + } + return true; + } + catch ( CycleDetectedException e ) + { + problems.add( new DefaultModelProblem( + "Cycle detected between models at " + from + " and " + p, + ModelProblem.Severity.FATAL, + null, null, 0, 0, null, + e + ) ); + return false; + } + } + + @Override + public TransformerContext build() + { + return context; + } + + public FileModelSource 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, FileModelSource source ) + { + mappedSources.computeIfAbsent( new DefaultTransformerContext.GAKey( groupId, artifactId ), + k -> new HashSet<>() ).add( source ); + } +} From 2d0e44b47e7a3ddf22ed75524fb29b046e44c303 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 15 Sep 2022 05:37:46 +0200 Subject: [PATCH 06/16] Add resolve to ProjectBuilderSource to be able to wrap ModelSource2 --- .../api/services/ProjectBuilderSource.java | 14 +++++ .../internal/impl/DefaultProjectBuilder.java | 53 +++++++++++++------ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderSource.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderSource.java index 1add18cb12fb..579097f1d871 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderSource.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderSource.java @@ -35,4 +35,18 @@ public interface ProjectBuilderSource InputStream getInputStream() throws IOException; String getLocation(); + + /** + * Returns a new source identified by a relative path. Implementation MUST + * be able to accept relPath parameter values that + *

    + *
  • use either / or \ file path separator
  • + *
  • have .. parent directory references
  • + *
  • point either at file or directory.
  • + *
+ * + * @param relative is the path of the requested source relative to this source. + * @return related source or null if no such source. + */ + ProjectBuilderSource resolve( String relative ); } diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java index d9911c20148d..2fed6e25b4b4 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.nio.file.Path; import java.util.Collection; import java.util.List; @@ -46,7 +47,7 @@ import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.model.building.ModelProblem; -import org.apache.maven.model.building.ModelSource; +import org.apache.maven.model.building.ModelSource2; import org.apache.maven.project.DefaultProjectBuildingRequest; import org.apache.maven.project.ProjectBuildingException; import org.apache.maven.project.ProjectBuildingRequest; @@ -88,20 +89,7 @@ public ProjectBuilderResult build( ProjectBuilderRequest request ) else if ( request.getSource().isPresent() ) { ProjectBuilderSource source = request.getSource().get(); - ModelSource modelSource = new ModelSource() - { - @Override - public InputStream getInputStream() throws IOException - { - return source.getInputStream(); - } - - @Override - public String getLocation() - { - return source.getLocation(); - } - }; + ModelSource2 modelSource = new ProjectBuilderSourceWrapper( source ); res = builder.build( modelSource, req ); } else if ( request.getArtifact().isPresent() ) @@ -264,4 +252,39 @@ public Node getRoot() throw new ProjectBuilderException( "Unable to build project", e ); } } + + private static class ProjectBuilderSourceWrapper implements ModelSource2 + { + private final ProjectBuilderSource source; + + ProjectBuilderSourceWrapper( ProjectBuilderSource source ) + { + this.source = source; + } + + @Override + public InputStream getInputStream() throws IOException + { + return source.getInputStream(); + } + + @Override + public String getLocation() + { + return source.getLocation(); + } + + @Override + public ModelSource2 getRelatedSource( String relPath ) + { + ProjectBuilderSource rel = source.resolve( relPath ); + return rel != null ? new ProjectBuilderSourceWrapper( rel ) : null; + } + + @Override + public URI getLocationURI() + { + return null; + } + } } From 5a5bbd271b9fdc94555a38cf4af83528ffa7fd8b Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 15 Sep 2022 05:44:09 +0200 Subject: [PATCH 07/16] Remove unused ModelBuildingResult parameter in a few internal methods --- .../maven/model/building/DefaultModelBuilder.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 867b3177ce1e..84dbf7d6808d 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 @@ -1230,7 +1230,7 @@ private Model interpolateModel( Model model, ModelBuildingRequest request, Model } private ModelData readParent( Model childModel, Source childSource, ModelBuildingRequest request, - ModelBuildingResult result, DefaultModelProblemCollector problems ) + DefaultModelProblemCollector problems ) throws ModelBuildingException { ModelData parentData = null; @@ -1238,10 +1238,10 @@ private ModelData readParent( Model childModel, Source childSource, ModelBuildin Parent parent = childModel.getParent(); if ( parent != null ) { - parentData = readParentLocally( childModel, childSource, request, result, problems ); + parentData = readParentLocally( childModel, childSource, request, problems ); if ( parentData == null ) { - parentData = readParentExternally( childModel, request, result, problems ); + parentData = readParentExternally( childModel, request, problems ); } Model parentModel = parentData.getModel(); @@ -1258,7 +1258,7 @@ private ModelData readParent( Model childModel, Source childSource, ModelBuildin } private ModelData readParentLocally( Model childModel, Source childSource, ModelBuildingRequest request, - ModelBuildingResult result, DefaultModelProblemCollector problems ) + DefaultModelProblemCollector problems ) throws ModelBuildingException { final Parent parent = childModel.getParent(); @@ -1413,7 +1413,7 @@ private ModelSource getParentPomFile( Model childModel, Source source ) } private ModelData readParentExternally( Model childModel, ModelBuildingRequest request, - ModelBuildingResult result, DefaultModelProblemCollector problems ) + DefaultModelProblemCollector problems ) throws ModelBuildingException { problems.setSource( childModel ); From 656a333b58850b93fa851d1933a3c14c49ecb955 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 15 Sep 2022 05:45:19 +0200 Subject: [PATCH 08/16] Switch a few computations to the immutable api --- .../model/building/DefaultModelBuilder.java | 223 +++++++++--------- .../model/profile/DefaultProfileInjector.java | 20 +- .../model/profile/DefaultProfileSelector.java | 10 + .../maven/model/profile/ProfileInjector.java | 4 + .../maven/model/profile/ProfileSelector.java | 4 + 5 files changed, 149 insertions(+), 112 deletions(-) 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 84dbf7d6808d..ab30976ef7ec 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 @@ -39,6 +39,7 @@ import java.util.concurrent.ForkJoinTask; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; @@ -50,7 +51,6 @@ import org.apache.maven.building.Source; import org.apache.maven.feature.Features; import org.apache.maven.model.Activation; -import org.apache.maven.model.ActivationFile; import org.apache.maven.model.Build; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; @@ -61,7 +61,6 @@ import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginManagement; import org.apache.maven.model.Profile; -import org.apache.maven.model.Repository; import org.apache.maven.model.building.ModelProblem.Severity; import org.apache.maven.model.building.ModelProblem.Version; import org.apache.maven.model.composition.DependencyManagementImporter; @@ -559,55 +558,42 @@ private Model readEffectiveModel( final ModelBuildingRequest request, final Defa String modelId = currentData.getId(); result.addModelId( modelId ); - Model rawModel = currentData.getModel(); - 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(); - - problems.setSource( tmpModel ); + Model model = currentData.getModel(); + result.setRawModel( modelId, model ); + problems.setSource( model ); + org.apache.maven.api.model.Model modelv4 = model.getDelegate(); // model normalization - tmpModel = new Model( modelNormalizer.mergeDuplicates( tmpModel.getDelegate(), request, problems ) ); + modelv4 = modelNormalizer.mergeDuplicates( modelv4, request, problems ); - profileActivationContext.setProjectProperties( tmpModel.getProperties() ); + // profile activation + profileActivationContext.setProjectProperties( modelv4.getProperties() ); - Map interpolatedActivations = getInterpolatedActivations( rawModel, - profileActivationContext, - problems ); - injectProfileActivations( tmpModel, interpolatedActivations ); + modelv4 = interpolateActivations( modelv4, profileActivationContext, problems ); // profile injection - for ( Profile activeProfile : result.getActivePomProfiles( modelId ) ) + List activePomProfiles = profileSelector.getActiveProfilesV4( + modelv4.getProfiles(), profileActivationContext, problems ); + result.setActivePomProfiles( modelId, + activePomProfiles.stream().map( Profile::new ).collect( Collectors.toList() ) ); + for ( org.apache.maven.api.model.Profile activeProfile : activePomProfiles ) { - profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); - } - - if ( currentData == resultData ) - { - for ( Profile activeProfile : activeExternalProfiles ) - { - profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); - } - result.setEffectiveModel( tmpModel ); + modelv4 = profileInjector.injectProfile( modelv4, activeProfile, request, problems ); } - lineage.add( tmpModel ); + lineage.add( new Model( modelv4 ) ); if ( currentData == superData ) { break; } - configureResolver( request.getModelResolver(), tmpModel, problems ); + // add repositories specified by the current model so that we can resolve the parent + configureResolver( request.getModelResolver(), modelv4, problems, false ); + // we pass a cloned model, so that resolving the parent version does not affect the returned model ModelData parentData = - readParent( currentData.getModel(), currentData.getSource(), request, result, problems ); + readParent( new Model( modelv4 ), currentData.getSource(), request, problems ); if ( parentData == null ) { @@ -633,7 +619,14 @@ else if ( !parentIds.add( parentData.getId() ) ) } } - problems.setSource( result.getRawModel() ); + // inject external profile into current model + Model tmpModel = lineage.get( 0 ); + for ( Profile activeProfile : activeExternalProfiles ) + { + tmpModel.update( profileInjector.injectProfile( + tmpModel.getDelegate(), activeProfile.getDelegate(), request, problems ) ); + } + checkPluginVersions( lineage, request, problems ); // inheritance assembly @@ -653,54 +646,87 @@ else if ( !parentIds.add( parentData.getId() ) ) result.setEffectiveModel( resultModel ); // Now the fully interpolated model is available: reconfigure the resolver - configureResolver( request.getModelResolver(), resultModel, problems, true ); + configureResolver( request.getModelResolver(), resultModel.getDelegate(), problems, true ); return resultModel; } - private Map getInterpolatedActivations( Model rawModel, - DefaultProfileActivationContext context, - DefaultModelProblemCollector problems ) + private org.apache.maven.api.model.Model interpolateActivations( org.apache.maven.api.model.Model model, + DefaultProfileActivationContext context, + DefaultModelProblemCollector problems ) { - Map interpolatedActivations = getProfileActivations( rawModel, true ); - for ( Activation activation : interpolatedActivations.values() ) + boolean modified = false; + List profiles = new ArrayList<>(); + for ( org.apache.maven.api.model.Profile profile : model.getProfiles() ) { - if ( activation.getFile() != null ) + org.apache.maven.api.model.Activation activation = profile.getActivation(); + if ( activation != null ) { - replaceWithInterpolatedValue( activation.getFile(), context, problems ); + org.apache.maven.api.model.ActivationFile file = activation.getFile(); + if ( file != null ) + { + String oldExists = file.getExists(); + if ( isNotEmpty( oldExists ) ) + { + try + { + String newExists = interpolate( oldExists, context ); + if ( !Objects.equals( oldExists, newExists ) ) + { + profile = profile.withActivation( activation.withFile( file.withExists( newExists ) ) ); + modified = true; + } + } + catch ( InterpolationException e ) + { + addInterpolationProblem( problems, file, oldExists, e, "exists" ); + } + } + else + { + String oldMissing = file.getMissing(); + if ( isNotEmpty( oldMissing ) ) + { + try + { + String newMissing = interpolate( oldMissing, context ); + if ( !Objects.equals( oldMissing, newMissing ) ) + { + profile = profile.withActivation( + activation.withFile( file.withMissing( newMissing ) ) ); + modified = true; + } + } + catch ( InterpolationException e ) + { + addInterpolationProblem( problems, file, oldMissing, e, "missing" ); + } + } + } + } + profiles.add( profile ); } } - return interpolatedActivations; + return modified ? model.withProfiles( profiles ) : model; } - private void replaceWithInterpolatedValue( ActivationFile activationFile, ProfileActivationContext context, - DefaultModelProblemCollector problems ) + private static void addInterpolationProblem( DefaultModelProblemCollector problems, + org.apache.maven.api.model.ActivationFile file, String path, + InterpolationException e, String locationKey ) { - try - { - if ( isNotEmpty( activationFile.getExists() ) ) - { - String path = activationFile.getExists(); - String absolutePath = profileActivationFilePathInterpolator.interpolate( path, context ); - activationFile.setExists( absolutePath ); - } - else if ( isNotEmpty( activationFile.getMissing() ) ) - { - String path = activationFile.getMissing(); - String absolutePath = profileActivationFilePathInterpolator.interpolate( path, context ); - activationFile.setMissing( absolutePath ); - } - } - catch ( InterpolationException e ) - { - String path = isNotEmpty( - activationFile.getExists() ) ? activationFile.getExists() : activationFile.getMissing(); + problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) + .setMessage( "Failed to interpolate file location " + path + ": " + + e.getMessage() ) + .setLocation( Optional.ofNullable( file.getLocation( locationKey ) ) + .map( InputLocation::new ).orElse( null ) ) + .setException( e ) ); + } - problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( - "Failed to interpolate file location " + path + ": " + e.getMessage() ).setLocation( - activationFile.getLocation( isNotEmpty( activationFile.getExists() ) ? "exists" : "missing" ) ) - .setException( e ) ); - } + private String interpolate( String path, ProfileActivationContext context ) throws InterpolationException + { + return isNotEmpty( path ) + ? profileActivationFilePathInterpolator.interpolate( path, context ) + : path; } private static boolean isNotEmpty( String string ) @@ -1053,34 +1079,23 @@ private DefaultProfileActivationContext getProfileActivationContext( ModelBuildi return context; } - private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems ) + private void configureResolver( ModelResolver modelResolver, org.apache.maven.api.model.Model model, + DefaultModelProblemCollector problems, boolean replaceRepositories ) { - configureResolver( modelResolver, model, problems, false ); - } - - private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems, - boolean replaceRepositories ) - { - if ( modelResolver == null ) - { - return; - } - - problems.setSource( model ); - - List repositories = model.getRepositories(); - - for ( Repository repository : repositories ) + if ( modelResolver != null ) { - try - { - modelResolver.addRepository( repository, replaceRepositories ); - } - catch ( InvalidRepositoryException e ) + for ( org.apache.maven.api.model.Repository repository : model.getRepositories() ) { - problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) - .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() ) - .setLocation( repository.getLocation( "" ) ).setException( e ) ); + try + { + modelResolver.addRepository( repository, replaceRepositories ); + } + catch ( InvalidRepositoryException e ) + { + problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) + .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() ) + .setLocation( new InputLocation( repository.getLocation( "" ) ) ).setException( e ) ); + } } } } @@ -1609,22 +1624,16 @@ private DependencyManagement loadDependencyManagement( Model model, ModelBuildin org.apache.maven.api.model.DependencyManagement importMgmt = cache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, - () -> Optional.ofNullable( doLoadDependencyManagement( - model, request, problems, dependency, groupId, artifactId, version, importIds ) ) - .map( DependencyManagement::getDelegate ) - .orElse( null ) ); + () -> doLoadDependencyManagement( + model, request, problems, dependency, groupId, artifactId, version, importIds ) ); return importMgmt != null ? new DependencyManagement( importMgmt ) : null; } @SuppressWarnings( "checkstyle:parameternumber" ) - private DependencyManagement doLoadDependencyManagement( Model model, ModelBuildingRequest request, - DefaultModelProblemCollector problems, - Dependency dependency, - String groupId, - String artifactId, - String version, - Collection importIds ) + private org.apache.maven.api.model.DependencyManagement doLoadDependencyManagement( + Model model, ModelBuildingRequest request, DefaultModelProblemCollector problems, Dependency dependency, + String groupId, String artifactId, String version, Collection importIds ) { DependencyManagement importMgmt; final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver(); @@ -1708,7 +1717,7 @@ private DependencyManagement doLoadDependencyManagement( Model model, ModelBuild { importMgmt = new DependencyManagement(); } - return importMgmt; + return importMgmt.getDelegate(); } private static T cache( ModelCache cache, String groupId, String artifactId, String version, diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java index ec7eb25c763a..bba51c69e38a 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java @@ -35,6 +35,7 @@ import org.apache.maven.api.model.Plugin; import org.apache.maven.api.model.PluginContainer; import org.apache.maven.api.model.PluginExecution; +import org.apache.maven.api.model.Profile; import org.apache.maven.api.model.ReportPlugin; import org.apache.maven.api.model.ReportSet; import org.apache.maven.api.model.Reporting; @@ -61,22 +62,31 @@ public void injectProfile( org.apache.maven.model.Model model, org.apache.maven.model.Profile profile, ModelBuildingRequest request, ModelProblemCollector problems ) + { + model.update( injectProfile( + model.getDelegate(), profile != null ? profile.getDelegate() : null, request, problems ) ); + } + + @Override + public Model injectProfile( Model model, Profile profile, ModelBuildingRequest request, + ModelProblemCollector problems ) { if ( profile != null ) { - Model.Builder builder = Model.newBuilder( model.getDelegate() ); - merger.mergeModelBase( builder, model.getDelegate(), profile.getDelegate() ); + Model.Builder builder = Model.newBuilder( model ); + merger.mergeModelBase( builder, model, profile ); if ( profile.getBuild() != null ) { - Build build = model.getBuild() != null ? model.getBuild().getDelegate() : Build.newInstance(); + Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance(); Build.Builder bbuilder = Build.newBuilder( build ); - merger.mergeBuildBase( bbuilder, build, profile.getBuild().getDelegate() ); + merger.mergeBuildBase( bbuilder, build, profile.getBuild() ); builder.build( bbuilder.build() ); } - model.update( builder.build() ); + return builder.build(); } + return model; } /** diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java index 75dc10f794a8..6580aae0fb41 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; @@ -64,6 +65,15 @@ public DefaultProfileSelector addProfileActivator( ProfileActivator profileActiv return this; } + @Override + public List getActiveProfilesV4( + Collection profiles, ProfileActivationContext context, + ModelProblemCollector problems ) + { + return getActiveProfiles( profiles.stream().map( Profile::new ).collect( Collectors.toList() ), + context, problems ).stream().map( Profile::getDelegate ).collect( Collectors.toList() ); + } + @Override public List getActiveProfiles( Collection profiles, ProfileActivationContext context, ModelProblemCollector problems ) diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java index fbd7ddf02285..e6c684a41890 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java @@ -43,4 +43,8 @@ public interface ProfileInjector */ void injectProfile( Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems ); + org.apache.maven.api.model.Model injectProfile( + org.apache.maven.api.model.Model model, + org.apache.maven.api.model.Profile profile, + ModelBuildingRequest request, ModelProblemCollector problems ); } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java index 53ea8d9c04d0..834a2b5cf096 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java @@ -46,4 +46,8 @@ public interface ProfileSelector List getActiveProfiles( Collection profiles, ProfileActivationContext context, ModelProblemCollector problems ); + List getActiveProfilesV4( + Collection profiles, + ProfileActivationContext context, + ModelProblemCollector problems ); } From f4028e3fbcefc787a82b3150c56cbc212e384847 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 15 Sep 2022 15:09:27 +0200 Subject: [PATCH 09/16] Add caches for model inheritance and profile injection --- .../model/building/DefaultModelBuilder.java | 58 +++++++++++-------- .../DefaultInheritanceAssembler.java | 16 ++++- .../model/profile/DefaultProfileInjector.java | 43 ++++++++++---- .../maven/model/profile/ProfileInjector.java | 7 +++ .../superpom/DefaultSuperPomProvider.java | 13 ++--- 5 files changed, 94 insertions(+), 43 deletions(-) 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 ab30976ef7ec..22a6be7defdf 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 @@ -569,17 +569,15 @@ private Model readEffectiveModel( final ModelBuildingRequest request, final Defa // profile activation profileActivationContext.setProjectProperties( modelv4.getProperties() ); - modelv4 = interpolateActivations( modelv4, profileActivationContext, problems ); + List interpolatedProfiles = + interpolateActivations( modelv4.getProfiles(), profileActivationContext, problems ); // profile injection List activePomProfiles = profileSelector.getActiveProfilesV4( - modelv4.getProfiles(), profileActivationContext, problems ); + interpolatedProfiles, profileActivationContext, problems ); result.setActivePomProfiles( modelId, activePomProfiles.stream().map( Profile::new ).collect( Collectors.toList() ) ); - for ( org.apache.maven.api.model.Profile activeProfile : activePomProfiles ) - { - modelv4 = profileInjector.injectProfile( modelv4, activeProfile, request, problems ); - } + modelv4 = profileInjector.injectProfiles( modelv4, activePomProfiles, request, problems ); lineage.add( new Model( modelv4 ) ); @@ -619,14 +617,21 @@ else if ( !parentIds.add( parentData.getId() ) ) } } - // inject external profile into current model Model tmpModel = lineage.get( 0 ); - for ( Profile activeProfile : activeExternalProfiles ) + + // inject interpolated activations + List interpolated = interpolateActivations( + tmpModel.getDelegate().getProfiles(), profileActivationContext, problems ); + if ( interpolated != tmpModel.getDelegate().getProfiles() ) { - tmpModel.update( profileInjector.injectProfile( - tmpModel.getDelegate(), activeProfile.getDelegate(), request, problems ) ); + tmpModel.update( tmpModel.getDelegate().withProfiles( interpolated ) ); } + // inject external profile into current model + tmpModel.update( profileInjector.injectProfiles( tmpModel.getDelegate(), + activeExternalProfiles.stream().map( Profile::getDelegate ).collect( Collectors.toList() ), + request, problems ) ); + checkPluginVersions( lineage, request, problems ); // inheritance assembly @@ -651,14 +656,15 @@ else if ( !parentIds.add( parentData.getId() ) ) return resultModel; } - private org.apache.maven.api.model.Model interpolateActivations( org.apache.maven.api.model.Model model, - DefaultProfileActivationContext context, - DefaultModelProblemCollector problems ) + private List interpolateActivations( + List profiles, + DefaultProfileActivationContext context, + DefaultModelProblemCollector problems ) { - boolean modified = false; - List profiles = new ArrayList<>(); - for ( org.apache.maven.api.model.Profile profile : model.getProfiles() ) + List newProfiles = null; + for ( int index = 0; index < profiles.size(); index++ ) { + org.apache.maven.api.model.Profile profile = profiles.get( index ); org.apache.maven.api.model.Activation activation = profile.getActivation(); if ( activation != null ) { @@ -673,8 +679,12 @@ private org.apache.maven.api.model.Model interpolateActivations( org.apache.mave String newExists = interpolate( oldExists, context ); if ( !Objects.equals( oldExists, newExists ) ) { - profile = profile.withActivation( activation.withFile( file.withExists( newExists ) ) ); - modified = true; + if ( newProfiles == null ) + { + newProfiles = new ArrayList<>( profiles ); + } + newProfiles.set( index, profile.withActivation( + activation.withFile( file.withExists( newExists ) ) ) ); } } catch ( InterpolationException e ) @@ -692,9 +702,12 @@ private org.apache.maven.api.model.Model interpolateActivations( org.apache.mave String newMissing = interpolate( oldMissing, context ); if ( !Objects.equals( oldMissing, newMissing ) ) { - profile = profile.withActivation( - activation.withFile( file.withMissing( newMissing ) ) ); - modified = true; + if ( newProfiles == null ) + { + newProfiles = new ArrayList<>( profiles ); + } + newProfiles.set( index, profile.withActivation( + activation.withFile( file.withMissing( newMissing ) ) ) ); } } catch ( InterpolationException e ) @@ -704,10 +717,9 @@ private org.apache.maven.api.model.Model interpolateActivations( org.apache.mave } } } - profiles.add( profile ); } } - return modified ? model.withProfiles( profiles ) : model; + return newProfiles != null ? newProfiles : profiles; } private static void addInterpolationProblem( DefaultModelProblemCollector problems, diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java index 1002f29ff743..3681f7ede0e5 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java @@ -20,10 +20,12 @@ */ import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; import javax.inject.Named; import javax.inject.Singleton; @@ -52,15 +54,25 @@ public class DefaultInheritanceAssembler implements InheritanceAssembler { - private InheritanceModelMerger merger = new InheritanceModelMerger(); - private static final String CHILD_DIRECTORY = "child-directory"; private static final String CHILD_DIRECTORY_PROPERTY = "project.directory"; + private static final Map> CACHE = Collections.synchronizedMap( new WeakHashMap<>() ); + + private InheritanceModelMerger merger = new InheritanceModelMerger(); + @Override public Model assembleModelInheritance( Model child, Model parent, ModelBuildingRequest request, ModelProblemCollector problems ) + { + // The use of the cache using a WeakHashMap is permitted because Model does not define equals/hashCode + return CACHE + .computeIfAbsent( child, c -> Collections.synchronizedMap( new WeakHashMap<>() ) ) + .computeIfAbsent( parent, c -> doAssemble( child, parent ) ); + } + + private Model doAssemble( Model child, Model parent ) { Map hints = new HashMap<>(); String childPath = child.getProperties().getOrDefault( CHILD_DIRECTORY_PROPERTY, child.getArtifactId() ); diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java index bba51c69e38a..f571b867d2df 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java @@ -27,6 +27,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.api.model.Build; import org.apache.maven.api.model.BuildBase; @@ -55,6 +57,9 @@ public class DefaultProfileInjector implements ProfileInjector { + private static final Map, Model>> CACHE + = Collections.synchronizedMap( new WeakHashMap<>() ); + private ProfileModelMerger merger = new ProfileModelMerger(); @Override @@ -71,20 +76,36 @@ public void injectProfile( org.apache.maven.model.Model model, public Model injectProfile( Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems ) { - if ( profile != null ) - { - Model.Builder builder = Model.newBuilder( model ); - merger.mergeModelBase( builder, model, profile ); + return injectProfiles( model, Collections.singletonList( profile ), request, problems ); + } + + @Override + public Model injectProfiles( Model model, List profiles, ModelBuildingRequest request, + ModelProblemCollector problems ) + { + return CACHE.computeIfAbsent( model, k -> new ConcurrentHashMap<>() ) + .computeIfAbsent( profiles, l -> doInjectProfiles( model, profiles ) ); + } - if ( profile.getBuild() != null ) + private Model doInjectProfiles( Model model, List profiles ) + { + for ( Profile profile : profiles ) + { + if ( profile != null ) { - Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance(); - Build.Builder bbuilder = Build.newBuilder( build ); - merger.mergeBuildBase( bbuilder, build, profile.getBuild() ); - builder.build( bbuilder.build() ); - } + Model.Builder builder = Model.newBuilder( model ); + merger.mergeModelBase( builder, model, profile ); - return builder.build(); + if ( profile.getBuild() != null ) + { + Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance(); + Build.Builder bbuilder = Build.newBuilder( build ); + merger.mergeBuildBase( bbuilder, build, profile.getBuild() ); + builder.build( bbuilder.build() ); + } + + model = builder.build(); + } } return model; } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java index e6c684a41890..01f1ff7f66aa 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java @@ -19,6 +19,8 @@ * under the License. */ +import java.util.List; + import org.apache.maven.model.Model; import org.apache.maven.model.Profile; import org.apache.maven.model.building.ModelBuildingRequest; @@ -47,4 +49,9 @@ org.apache.maven.api.model.Model injectProfile( org.apache.maven.api.model.Model model, org.apache.maven.api.model.Profile profile, ModelBuildingRequest request, ModelProblemCollector problems ); + + org.apache.maven.api.model.Model injectProfiles( + org.apache.maven.api.model.Model model, + List profiles, + ModelBuildingRequest request, ModelProblemCollector problems ); } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/superpom/DefaultSuperPomProvider.java b/maven-model-builder/src/main/java/org/apache/maven/model/superpom/DefaultSuperPomProvider.java index bb6a82025677..227004532385 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/superpom/DefaultSuperPomProvider.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/superpom/DefaultSuperPomProvider.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.inject.Named; @@ -48,7 +49,7 @@ public class DefaultSuperPomProvider /** * The cached super POM, lazily created. */ - private Model superModel; + private static final Map SUPER_MODELS = new ConcurrentHashMap<>(); @Inject public DefaultSuperPomProvider( ModelProcessor modelProcessor ) @@ -59,9 +60,9 @@ public DefaultSuperPomProvider( ModelProcessor modelProcessor ) @Override public Model getSuperModel( String version ) { - if ( superModel == null ) + return SUPER_MODELS.computeIfAbsent( version, v -> { - String resource = "/org/apache/maven/model/pom-" + version + ".xml"; + String resource = "/org/apache/maven/model/pom-" + v + ".xml"; InputStream is = getClass().getResourceAsStream( resource ); @@ -82,16 +83,14 @@ public Model getSuperModel( String version ) modelId, getClass().getResource( resource ).toExternalForm() ); options.put( ModelProcessor.INPUT_SOURCE, inputSource ); - superModel = modelProcessor.read( is, options ); + return modelProcessor.read( is, options ); } catch ( IOException e ) { throw new IllegalStateException( "The super POM " + resource + " is damaged" + ", please verify the integrity of your Maven installation", e ); } - } - - return superModel; + } ); } } From aed773edb6b1f2dcc8384e9d275f6a98b518afe9 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 16 Sep 2022 11:37:34 +0200 Subject: [PATCH 10/16] Holders now cause deadlock, just delegate the synchronization to the ModelBuilder's cache --- .../building/DefaultTransformerContext.java | 100 +----------------- .../DefaultTransformerContextBuilder.java | 44 ++++---- 2 files changed, 25 insertions(+), 119 deletions(-) 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 index 459e16c4e2b5..010969178ae8 100644 --- 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 @@ -21,9 +21,7 @@ import java.nio.file.Path; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; import org.apache.maven.model.Model; @@ -36,64 +34,9 @@ class DefaultTransformerContext implements TransformerContext { final Map userProperties = new ConcurrentHashMap<>(); - final Map modelByPath = new ConcurrentHashMap<>(); + final Map modelByPath = new ConcurrentHashMap<>(); - final Map modelByGA = new ConcurrentHashMap<>(); - - public static class Holder - { - private volatile boolean set; - private volatile Model model; - - Holder() - { - } - - public static Model deref( Holder holder ) - { - return holder != null ? holder.get() : null; - } - - public Model get() - { - if ( !set ) - { - synchronized ( this ) - { - if ( !set ) - { - try - { - this.wait(); - } - catch ( InterruptedException e ) - { - // Ignore - } - } - } - } - return model; - } - - public Model computeIfAbsent( Supplier supplier ) - { - if ( !set ) - { - synchronized ( this ) - { - if ( !set ) - { - this.set = true; - this.model = supplier.get(); - this.notifyAll(); - } - } - } - return model; - } - - } + final Map modelByGA = new ConcurrentHashMap<>(); @Override public String getUserProperty( String key ) @@ -104,48 +47,13 @@ public String getUserProperty( String key ) @Override public Model getRawModel( Path from, Path p ) { - return Holder.deref( modelByPath.get( p ) ); + return modelByPath.get( p ); } @Override public Model getRawModel( Path from, String groupId, String artifactId ) { - return Holder.deref( modelByGA.get( new GAKey( groupId, artifactId ) ) ); + return modelByGA.get( 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/DefaultTransformerContextBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java index 54a237a12425..67fa0f2c5c55 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java @@ -42,8 +42,7 @@ class DefaultTransformerContextBuilder implements TransformerContextBuilder { private final DefaultTransformerContext context = new DefaultTransformerContext(); - private final Map> mappedSources - = new ConcurrentHashMap<>( 64 ); + private final Map> mappedSources = new ConcurrentHashMap<>( 64 ); private final DAG dag = new DAG(); private final DefaultModelBuilder defaultModelBuilder; @@ -73,17 +72,26 @@ public String getUserProperty( String key ) @Override public Model getRawModel( Path from, String gId, String aId ) { - return context.modelByGA.computeIfAbsent( new DefaultTransformerContext.GAKey( gId, aId ), - k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> findRawModel( from, gId, aId ) ); + Model model = findRawModel( from, gId, aId ); + if ( model != null ) + { + context.modelByGA.put( gId + ":" + aId, model ); + context.modelByPath.put( model.getPomFile().toPath(), model ); + } + return model; } @Override public Model getRawModel( Path from, Path path ) { - return context.modelByPath.computeIfAbsent( path, - k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> findRawModel( from, path ) ); + Model model = findRawModel( from, path ); + if ( model != null ) + { + String groupId = defaultModelBuilder.getGroupId( model ); + context.modelByGA.put( groupId + ":" + model.getArtifactId(), model ); + context.modelByPath.put( path, model ); + } + return model; } private Model findRawModel( Path from, String groupId, String artifactId ) @@ -99,11 +107,7 @@ private Model findRawModel( Path from, String groupId, String artifactId ) { ModelBuildingRequest gaBuildingRequest = new DefaultModelBuildingRequest( request ) .setModelSource( source ); - Model model = defaultModelBuilder.readRawModel( gaBuildingRequest, problems ); - Path path = source.getFile().toPath(); - context.modelByPath.computeIfAbsent( path, k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> model ); - return model; + return defaultModelBuilder.readRawModel( gaBuildingRequest, problems ); } catch ( ModelBuildingException e ) { @@ -131,12 +135,7 @@ private Model findRawModel( Path from, Path p ) try { - Model model = defaultModelBuilder.readRawModel( req, problems ); - DefaultTransformerContext.GAKey key = - new DefaultTransformerContext.GAKey( defaultModelBuilder.getGroupId( model ), model.getArtifactId() ); - context.modelByGA.computeIfAbsent( key, k -> new DefaultTransformerContext.Holder() ) - .computeIfAbsent( () -> model ); - return model; + return defaultModelBuilder.readRawModel( req, problems ); } catch ( ModelBuildingException e ) { @@ -177,8 +176,7 @@ public TransformerContext build() public FileModelSource getSource( String groupId, String artifactId ) { - Set sources = mappedSources.get( - new DefaultTransformerContext.GAKey( groupId, artifactId ) ); + Set sources = mappedSources.get( groupId + ":" + artifactId ); if ( sources == null ) { return null; @@ -193,7 +191,7 @@ public FileModelSource getSource( String groupId, String artifactId ) public void putSource( String groupId, String artifactId, FileModelSource source ) { - mappedSources.computeIfAbsent( new DefaultTransformerContext.GAKey( groupId, artifactId ), - k -> new HashSet<>() ).add( source ); + mappedSources.computeIfAbsent( groupId + ":" + artifactId, k -> new HashSet<>() ) + .add( source ); } } From 78ac6ffb27c0dac66ff6438c3c8298b8235b4a65 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Mon, 3 Oct 2022 16:00:05 +0200 Subject: [PATCH 11/16] Try disabling cache in DefaultInheritanceAssembler --- .../model/inheritance/DefaultInheritanceAssembler.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java index 3681f7ede0e5..67ea4a56a631 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java @@ -67,9 +67,10 @@ public Model assembleModelInheritance( Model child, Model parent, ModelBuildingR ModelProblemCollector problems ) { // The use of the cache using a WeakHashMap is permitted because Model does not define equals/hashCode - return CACHE - .computeIfAbsent( child, c -> Collections.synchronizedMap( new WeakHashMap<>() ) ) - .computeIfAbsent( parent, c -> doAssemble( child, parent ) ); +// return CACHE +// .computeIfAbsent( child, c -> Collections.synchronizedMap( new WeakHashMap<>() ) ) +// .computeIfAbsent( parent, c -> doAssemble( child, parent ) ); + return doAssemble( child, parent ); } private Model doAssemble( Model child, Model parent ) From 957471473b679eae380cb321bb2a9c6399b1dda8 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 4 Oct 2022 15:09:21 +0200 Subject: [PATCH 12/16] Allow configuring the parallelism level --- .../apache/maven/project/DefaultProjectBuilder.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 d08f7e434be5..4e8f57dfa4b3 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 @@ -223,7 +223,17 @@ class BuildSession this.poolBuilder = new ReactorModelPool.Builder(); this.modelPool = poolBuilder.build(); this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder(); - this.forkJoinPool = new ForkJoinPool(); + int parallelism = Runtime.getRuntime().availableProcessors(); + try + { + String str = request.getUserProperties().getProperty( "maven.projectBuilder.parallelism" ); + parallelism = Integer.parseInt( str ); + } + catch ( Exception e ) + { + // ignore + } + this.forkJoinPool = new ForkJoinPool( parallelism ); } else { From 3e7b0edfe7456d1be103b7a636f1b33afffa3c58 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 6 Oct 2022 11:26:36 +0200 Subject: [PATCH 13/16] Fix compilation and cleanup DefaultInheritanceAssembler (cache removal) --- .../maven/model/building/DefaultModelBuilder.java | 2 +- .../inheritance/DefaultInheritanceAssembler.java | 15 +-------------- .../profile/DefaultProfileActivationContext.java | 11 ++--------- 3 files changed, 4 insertions(+), 24 deletions(-) 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 22a6be7defdf..e977b5b3e2d3 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 @@ -492,7 +492,7 @@ private void activateFileModel( final ModelBuildingRequest request, final Defaul profileActivationContext.setUserProperties( profileProps ); } - profileActivationContext.setProjectProperties( inputModel.getProperties() ); + profileActivationContext.setProjectProperties( inputModel.getDelegate().getProperties() ); problems.setSource( inputModel ); List activePomProfiles = profileSelector.getActiveProfiles( inputModel.getProfiles(), profileActivationContext, problems ); diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java index 67ea4a56a631..9ed5b420a456 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java @@ -20,12 +20,10 @@ */ import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.WeakHashMap; import javax.inject.Named; import javax.inject.Singleton; @@ -58,22 +56,11 @@ public class DefaultInheritanceAssembler private static final String CHILD_DIRECTORY_PROPERTY = "project.directory"; - private static final Map> CACHE = Collections.synchronizedMap( new WeakHashMap<>() ); - - private InheritanceModelMerger merger = new InheritanceModelMerger(); + private final InheritanceModelMerger merger = new InheritanceModelMerger(); @Override public Model assembleModelInheritance( Model child, Model parent, ModelBuildingRequest request, ModelProblemCollector problems ) - { - // The use of the cache using a WeakHashMap is permitted because Model does not define equals/hashCode -// return CACHE -// .computeIfAbsent( child, c -> Collections.synchronizedMap( new WeakHashMap<>() ) ) -// .computeIfAbsent( parent, c -> doAssemble( child, parent ) ); - return doAssemble( child, parent ); - } - - private Model doAssemble( Model child, Model parent ) { Map hints = new HashMap<>(); String childPath = child.getProperties().getOrDefault( CHILD_DIRECTORY_PROPERTY, child.getArtifactId() ); diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileActivationContext.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileActivationContext.java index 4fb1b265e3ec..fd152691653c 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileActivationContext.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileActivationContext.java @@ -25,9 +25,6 @@ import java.util.Map; import java.util.Properties; -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toMap; - /** * Describes the environmental context used to determine the activation status of profiles. * @@ -227,15 +224,11 @@ public Map getProjectProperties() return projectProperties; } - public DefaultProfileActivationContext setProjectProperties( Properties projectProperties ) + public DefaultProfileActivationContext setProjectProperties( Map projectProperties ) { if ( projectProperties != null ) { - this.projectProperties = projectProperties.entrySet().stream() - .collect( - collectingAndThen( - toMap( k -> String.valueOf( k.getKey() ), v -> String.valueOf( v ) ), - Collections::unmodifiableMap ) ); + this.projectProperties = Collections.unmodifiableMap( projectProperties ); } else { From ff08b92bceb55ccacb240d7f008f6ae60c1afc42 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 6 Oct 2022 11:55:22 +0200 Subject: [PATCH 14/16] Clean up parallism configuration support --- .../maven/project/DefaultProjectBuilder.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) 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 4e8f57dfa4b3..cc95f643acd7 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 @@ -99,6 +99,8 @@ public class DefaultProjectBuilder implements ProjectBuilder { + private static final String BUILDER_PARALLELISM = "maven.projectBuilder.parallelism"; + private final Logger logger = LoggerFactory.getLogger( getClass() ); private final ModelBuilder modelBuilder; private final ModelProcessor modelProcessor; @@ -223,17 +225,7 @@ class BuildSession this.poolBuilder = new ReactorModelPool.Builder(); this.modelPool = poolBuilder.build(); this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder(); - int parallelism = Runtime.getRuntime().availableProcessors(); - try - { - String str = request.getUserProperties().getProperty( "maven.projectBuilder.parallelism" ); - parallelism = Integer.parseInt( str ); - } - catch ( Exception e ) - { - // ignore - } - this.forkJoinPool = new ForkJoinPool( parallelism ); + this.forkJoinPool = new ForkJoinPool( getParallelism( request ) ); } else { @@ -244,6 +236,28 @@ class BuildSession } } + private int getParallelism( ProjectBuildingRequest request ) + { + int parallelism = Runtime.getRuntime().availableProcessors(); + try + { + String str = request.getUserProperties().getProperty( BUILDER_PARALLELISM ); + if ( str == null ) + { + str = request.getSystemProperties().getProperty( BUILDER_PARALLELISM ); + } + if ( str != null ) + { + parallelism = Integer.parseInt( str ); + } + } + catch ( Exception e ) + { + // ignore + } + return parallelism; + } + ProjectBuildingResult build( File pomFile, ModelSource modelSource ) throws ProjectBuildingException { From d02652d1df2f515f0b644f748745952e22cf527b Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 6 Oct 2022 16:27:29 +0200 Subject: [PATCH 15/16] Fix memory consumption problem in the cache --- .../maven/model/profile/DefaultProfileInjector.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java index f571b867d2df..9eab4a3f36cc 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java @@ -60,6 +60,11 @@ public class DefaultProfileInjector private static final Map, Model>> CACHE = Collections.synchronizedMap( new WeakHashMap<>() ); + // In order for the weak hash map to work correctly, we must not hold any reference to + // the model used as the key. So we use a dummy model as a placeholder to indicate that + // we want to store the model used as they key. + private static final Model KEY = Model.newInstance(); + private ProfileModelMerger merger = new ProfileModelMerger(); @Override @@ -83,12 +88,14 @@ public Model injectProfile( Model model, Profile profile, ModelBuildingRequest r public Model injectProfiles( Model model, List profiles, ModelBuildingRequest request, ModelProblemCollector problems ) { - return CACHE.computeIfAbsent( model, k -> new ConcurrentHashMap<>() ) + Model result = CACHE.computeIfAbsent( model, k -> new ConcurrentHashMap<>() ) .computeIfAbsent( profiles, l -> doInjectProfiles( model, profiles ) ); + return result == KEY ? model : result; } private Model doInjectProfiles( Model model, List profiles ) { + Model orgModel = model; for ( Profile profile : profiles ) { if ( profile != null ) @@ -107,7 +114,7 @@ private Model doInjectProfiles( Model model, List profiles ) model = builder.build(); } } - return model; + return model == orgModel ? KEY : model; } /** From 39220217da758811fc097e40974412adc5e95c32 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 6 Oct 2022 22:03:27 +0200 Subject: [PATCH 16/16] Use a parallelStream instead of custom wrapping --- .../org/apache/maven/project/DefaultProjectBuilder.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 cc95f643acd7..860b7440d4a4 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 @@ -593,12 +593,8 @@ private InterimResult build( private List build( Map projectIndex, List interimResults ) { - List>> tasks = interimResults.stream().map( interimResult -> - ForkJoinTask.adapt( () -> doBuild( projectIndex, interimResult ) ) ) - .collect( Collectors.toList() ); - - return ForkJoinTask.invokeAll( tasks ).stream() - .map( ForkJoinTask::getRawResult ) + return interimResults.parallelStream() + .map( interimResult -> doBuild( projectIndex, interimResult ) ) .flatMap( List::stream ) .collect( Collectors.toList() ); }