MNG-5775 Make the project graph building code pluggable to allow for new/different implementations.

This commit is contained in:
Jason van Zyl 2014-09-03 11:48:28 -07:00
parent 7997634209
commit be3fb20032
25 changed files with 1271 additions and 561 deletions

View File

@ -52,6 +52,7 @@ import org.eclipse.aether.impl.RemoteRepositoryManager;
import org.eclipse.aether.impl.RepositoryEventDispatcher;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
@ -215,7 +216,6 @@ public class DefaultArtifactDescriptorReader
ArtifactDescriptorResult result = new ArtifactDescriptorResult( request );
Model model = loadPom( session, request, result );
if ( model != null )
{
Map<String, Object> config = session.getConfigProperties();
@ -303,6 +303,18 @@ public class DefaultArtifactDescriptorReader
}
Model model;
// hack: don't rebuild model if it was already loaded during reactor resolution
final WorkspaceReader workspace = session.getWorkspaceReader();
if ( workspace instanceof MavenWorkspaceReader )
{
model = ( (MavenWorkspaceReader) workspace ).findModel( pomArtifact );
if ( model != null )
{
return model;
}
}
try
{
ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest();

View File

@ -0,0 +1,32 @@
package org.apache.maven.repository.internal;
/*
* 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 org.apache.maven.model.Model;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.repository.WorkspaceReader;
public interface MavenWorkspaceReader
extends WorkspaceReader
{
Model findModel( Artifact artifact );
}

View File

@ -9,7 +9,7 @@ package org.apache.maven;
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@ -39,20 +38,15 @@ import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.graph.GraphBuilder;
import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
import org.apache.maven.lifecycle.internal.LifecycleStarter;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.ModelProblemUtils;
import org.apache.maven.model.building.ModelSource;
import org.apache.maven.model.building.UrlModelSource;
import org.apache.maven.model.building.Result;
import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
import org.apache.maven.session.scope.internal.SessionScope;
import org.codehaus.plexus.PlexusContainer;
@ -60,13 +54,13 @@ import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.dag.CycleDetectedException;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
import com.google.common.collect.Iterables;
/**
* @author Jason van Zyl
*/
@ -99,6 +93,9 @@ public class DefaultMaven
@Requirement
private DefaultRepositorySystemSessionFactory repositorySessionFactory;
@Requirement( hint = GraphBuilder.HINT )
private GraphBuilder graphBuilder;
@Override
public MavenExecutionResult execute( MavenExecutionRequest request )
{
@ -114,9 +111,18 @@ public class DefaultMaven
}
catch ( RuntimeException e )
{
result =
addExceptionToResult( new DefaultMavenExecutionResult(), new InternalErrorException( "Internal error: "
+ e, e ) );
//TODO Hack to make the cycle detection the same for the new graph builder
if ( e.getCause() instanceof ProjectCycleException )
{
result = addExceptionToResult( new DefaultMavenExecutionResult(), e.getCause() );
}
else
{
result = addExceptionToResult( new DefaultMavenExecutionResult(), new InternalErrorException(
"Internal error: "
+ e,
e ) );
}
}
finally
{
@ -140,17 +146,17 @@ public class DefaultMaven
// 6) Get reactor projects looking for general POM errors
//
// 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode.
// This ensures that the projects passed into the ReactorReader are only those specified.
// This ensures that the projects passed into the ReactorReader are only those specified.
//
// 8) Create ReactorReader with the getProjectMap( projects ). NOTE that getProjectMap(projects) is the code that
// checks for duplicate projects definitions in the build. Ideally this type of duplicate checking should be
// part of getting the reactor projects in 6). The duplicate checking is conflated with getProjectMap(projects).
// checks for duplicate projects definitions in the build. Ideally this type of duplicate checking should be
// part of getting the reactor projects in 6). The duplicate checking is conflated with getProjectMap(projects).
//
// 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session)
//
// 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is
// required after the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject
// instances, which may change dependencies which can, in turn, affect the build order.
// required after the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject
// instances, which may change dependencies which can, in turn, affect the build order.
//
// 11) Execute LifecycleStarter.start()
//
@ -171,7 +177,8 @@ public class DefaultMaven
}
//
// We enter the session scope right after the MavenSession creation and before any of the AbstractLifecycleParticipant lookups
// We enter the session scope right after the MavenSession creation and before any of the
// AbstractLifecycleParticipant lookups
// so that @SessionScoped components can be @Injected into AbstractLifecycleParticipants.
//
sessionScope.enter();
@ -198,7 +205,8 @@ public class DefaultMaven
{
try
{
for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject>emptyList() ) )
for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections
.<MavenProject>emptyList() ) )
{
listener.afterSessionStart( session );
}
@ -210,35 +218,14 @@ public class DefaultMaven
eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
List<MavenProject> projects;
try
Result<? extends ProjectDependencyGraph> graphResult = buildGraph( session, result );
if ( graphResult.hasErrors() )
{
projects = getProjectsForMavenReactor( session );
//
// Capture the full set of projects before any potential constraining is performed by --projects
//
session.setAllProjects( projects );
return addExceptionToResult( result,
Iterables.toArray( graphResult.getProblems(), ModelProblem.class )[0]
.getException() );
}
catch ( ProjectBuildingException e )
{
return addExceptionToResult( result, e );
}
validateProjects( projects );
//
// This creates the graph and trims the projects down based on the user request using something like:
//
// -pl project0,project2 eclipse:eclipse
//
ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true );
if ( result.hasExceptions() )
{
return result;
}
session.setProjects( projectDependencyGraph.getSortedProjects() );
try
{
@ -274,7 +261,7 @@ public class DefaultMaven
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try
{
for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( session.getProjects() ) )
{
Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
@ -298,7 +285,15 @@ public class DefaultMaven
// Note that participants may affect the topological order of the projects but it is
// not expected that a participant will add or remove projects from the session.
//
projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false );
graphResult = buildGraph( session, result );
if ( graphResult.hasErrors() )
{
return addExceptionToResult( result,
Iterables.toArray( graphResult.getProblems(), ModelProblem.class )[0]
.getException() );
}
try
{
@ -307,10 +302,6 @@ public class DefaultMaven
return result;
}
session.setProjects( projectDependencyGraph.getSortedProjects() );
session.setProjectDependencyGraph( projectDependencyGraph );
result.setTopologicallySortedProjects( session.getProjects() );
result.setProject( session.getTopLevelProject() );
@ -328,7 +319,7 @@ public class DefaultMaven
{
try
{
afterSessionEnd( projects, session );
afterSessionEnd( session.getProjects(), session );
}
catch ( MavenExecutionException e )
{
@ -357,7 +348,7 @@ public class DefaultMaven
Thread.currentThread().setContextClassLoader( originalClassLoader );
}
}
public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
{
return repositorySessionFactory.newRepositorySession( request );
@ -380,8 +371,7 @@ public class DefaultMaven
private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants( Collection<MavenProject> projects )
{
Collection<AbstractMavenLifecycleParticipant> lifecycleListeners =
new LinkedHashSet<AbstractMavenLifecycleParticipant>();
Collection<AbstractMavenLifecycleParticipant> lifecycleListeners = new LinkedHashSet<AbstractMavenLifecycleParticipant>();
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try
@ -436,72 +426,22 @@ public class DefaultMaven
return result;
}
private List<MavenProject> getProjectsForMavenReactor( MavenSession session )
throws ProjectBuildingException
private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
{
MavenExecutionRequest request = session.getRequest();
Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
List<MavenProject> projects = new ArrayList<MavenProject>();
// We have no POM file.
//
if ( request.getPom() == null )
for ( MavenProject project : projects )
{
ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) );
MavenProject project =
projectBuilder.build( modelSource, request.getProjectBuildingRequest() ).getProject();
project.setExecutionRoot( true );
projects.add( project );
request.setProjectPresent( false );
return projects;
}
List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() );
collectProjects( projects, files, request );
return projects;
}
private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
throws ProjectBuildingException
{
ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
List<ProjectBuildingResult> results =
projectBuilder.build( files, request.isRecursive(), projectBuildingRequest );
boolean problems = false;
for ( ProjectBuildingResult result : results )
{
projects.add( result.getProject() );
if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
for ( List<String> profileIds : project.getInjectedProfileIds().values() )
{
logger.warn( "" );
logger.warn( "Some problems were encountered while building the effective model for "
+ result.getProject().getId() );
for ( ModelProblem problem : result.getProblems() )
{
String loc = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( loc ) ? " @ " + loc : "" ) );
}
problems = true;
notActivatedProfileIds.removeAll( profileIds );
}
}
if ( problems )
for ( String notActivatedProfileId : notActivatedProfileIds )
{
logger.warn( "" );
logger.warn( "It is highly recommended to fix these problems"
+ " because they threaten the stability of your build." );
logger.warn( "" );
logger.warn( "For this reason, future Maven versions might no"
+ " longer support building such malformed projects." );
logger.warn( "" );
logger.warn( "The requested profile \"" + notActivatedProfileId
+ "\" could not be activated because it does not exist." );
}
}
@ -547,330 +487,36 @@ public class DefaultMaven
return index;
}
private void validateProjects( List<MavenProject> projects )
private Result<? extends ProjectDependencyGraph> buildGraph( MavenSession session, MavenExecutionResult result )
{
Map<String, MavenProject> projectsMap = new HashMap<String, MavenProject>();
for ( MavenProject p : projects )
Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build( session );
for ( ModelProblem problem : graphResult.getProblems() )
{
String projectKey = ArtifactUtils.key( p.getGroupId(), p.getArtifactId(), p.getVersion() );
projectsMap.put( projectKey, p );
}
for ( MavenProject project : projects )
{
// MNG-1911 / MNG-5572: Building plugins with extensions cannot be part of reactor
for ( Plugin plugin : project.getBuildPlugins() )
if ( problem.getSeverity() == ModelProblem.Severity.WARNING )
{
if ( plugin.isExtensions() )
{
String pluginKey =
ArtifactUtils.key( plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion() );
if ( projectsMap.containsKey( pluginKey ) )
{
logger.warn( project.getName() + " uses " + plugin.getKey()
+ " as extensions, which is not possible within the same reactor build. "
+ "This plugin was pulled from the local repository!" );
}
}
logger.warn( problem.toString() );
}
}
}
private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
{
Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
for ( MavenProject project : projects )
{
for ( List<String> profileIds : project.getInjectedProfileIds().values() )
else
{
notActivatedProfileIds.removeAll( profileIds );
logger.error( problem.toString() );
}
}
for ( String notActivatedProfileId : notActivatedProfileIds )
if ( !graphResult.hasErrors() )
{
logger.warn( "The requested profile \"" + notActivatedProfileId
+ "\" could not be activated because it does not exist." );
ProjectDependencyGraph projectDependencyGraph = graphResult.get();
session.setProjects( projectDependencyGraph.getSortedProjects() );
session.setAllProjects( projectDependencyGraph.getSortedProjects() );
session.setProjectDependencyGraph( projectDependencyGraph );
}
return graphResult;
}
@Deprecated // 5 January 2014
@Deprecated
// 5 January 2014
protected Logger getLogger()
{
return logger;
}
private ProjectDependencyGraph createProjectDependencyGraph( Collection<MavenProject> projects,
MavenExecutionRequest request,
MavenExecutionResult result, boolean trimming )
{
ProjectDependencyGraph projectDependencyGraph = null;
try
{
projectDependencyGraph = new DefaultProjectDependencyGraph( projects );
if ( trimming )
{
List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects();
activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request );
activeProjects = trimExcludedProjects( activeProjects, request );
activeProjects = trimResumedProjects( activeProjects, request );
if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() )
{
projectDependencyGraph =
new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects );
}
}
}
catch ( CycleDetectedException e )
{
String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
ProjectCycleException error = new ProjectCycleException( message, e );
addExceptionToResult( result, error );
}
catch ( org.apache.maven.project.DuplicateProjectException e )
{
addExceptionToResult( result, e );
}
catch ( MavenExecutionException e )
{
addExceptionToResult( result, e );
}
return projectDependencyGraph;
}
private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
MavenExecutionRequest request )
throws MavenExecutionException
{
List<MavenProject> result = projects;
if ( !request.getSelectedProjects().isEmpty() )
{
File reactorDirectory = null;
if ( request.getBaseDirectory() != null )
{
reactorDirectory = new File( request.getBaseDirectory() );
}
Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() );
for ( String selector : request.getSelectedProjects() )
{
MavenProject selectedProject = null;
for ( MavenProject project : projects )
{
if ( isMatchingProject( project, selector, reactorDirectory ) )
{
selectedProject = project;
break;
}
}
if ( selectedProject != null )
{
selectedProjects.add( selectedProject );
}
else
{
throw new MavenExecutionException( "Could not find the selected project in the reactor: "
+ selector, request.getPom() );
}
}
boolean makeUpstream = false;
boolean makeDownstream = false;
if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
{
makeUpstream = true;
}
else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
{
makeDownstream = true;
}
else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
{
makeUpstream = true;
makeDownstream = true;
}
else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
{
throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
request.getPom() );
}
if ( makeUpstream || makeDownstream )
{
for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) )
{
if ( makeUpstream )
{
selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
}
if ( makeDownstream )
{
selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
}
}
}
result = new ArrayList<MavenProject>( selectedProjects.size() );
for ( MavenProject project : projects )
{
if ( selectedProjects.contains( project ) )
{
result.add( project );
}
}
}
return result;
}
private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request )
throws MavenExecutionException
{
List<MavenProject> result = projects;
if ( !request.getExcludedProjects().isEmpty() )
{
File reactorDirectory = null;
if ( request.getBaseDirectory() != null )
{
reactorDirectory = new File( request.getBaseDirectory() );
}
Collection<MavenProject> excludedProjects = new LinkedHashSet<MavenProject>( projects.size() );
for ( String selector : request.getExcludedProjects() )
{
MavenProject excludedProject = null;
for ( MavenProject project : projects )
{
if ( isMatchingProject( project, selector, reactorDirectory ) )
{
excludedProject = project;
break;
}
}
if ( excludedProject != null )
{
excludedProjects.add( excludedProject );
}
else
{
throw new MavenExecutionException( "Could not find the selected project in the reactor: "
+ selector, request.getPom() );
}
}
result = new ArrayList<MavenProject>( projects.size() );
for ( MavenProject project : projects )
{
if ( !excludedProjects.contains( project ) )
{
result.add( project );
}
}
}
return result;
}
private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
throws MavenExecutionException
{
List<MavenProject> result = projects;
if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
{
File reactorDirectory = null;
if ( request.getBaseDirectory() != null )
{
reactorDirectory = new File( request.getBaseDirectory() );
}
String selector = request.getResumeFrom();
result = new ArrayList<MavenProject>( projects.size() );
boolean resumed = false;
for ( MavenProject project : projects )
{
if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
{
resumed = true;
}
if ( resumed )
{
result.add( project );
}
}
if ( !resumed )
{
throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
+ " vs " + projects, request.getPom() );
}
}
return result;
}
private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
{
// [groupId]:artifactId
if ( selector.indexOf( ':' ) >= 0 )
{
String id = ':' + project.getArtifactId();
if ( id.equals( selector ) )
{
return true;
}
id = project.getGroupId() + id;
if ( id.equals( selector ) )
{
return true;
}
}
// relative path, e.g. "sub", "../sub" or "."
else if ( reactorDirectory != null )
{
File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
if ( selectedProject.isFile() )
{
return selectedProject.equals( project.getFile() );
}
else if ( selectedProject.isDirectory() )
{
return selectedProject.equals( project.getBasedir() );
}
}
return false;
}
}

View File

@ -27,6 +27,11 @@ import org.codehaus.plexus.util.dag.CycleDetectedException;
public class ProjectCycleException
extends BuildFailureException
{
public ProjectCycleException( String message )
{
super( message );
}
public ProjectCycleException( String message, CycleDetectedException cause )
{
super( message, cause );

View File

@ -34,9 +34,10 @@ import javax.inject.Named;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Model;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.internal.MavenWorkspaceReader;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
@ -49,7 +50,7 @@ import org.eclipse.aether.util.artifact.ArtifactIdUtils;
@Named( ReactorReader.HINT )
@SessionScoped
class ReactorReader
implements WorkspaceReader
implements MavenWorkspaceReader
{
public static final String HINT = "reactor";
@ -136,6 +137,14 @@ class ReactorReader
return Collections.unmodifiableList( versions );
}
@Override
public Model findModel( Artifact artifact )
{
String projectKey = ArtifactUtils.key( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() );
MavenProject project = projectsByGAV.get( projectKey );
return project == null ? null : project.getModel();
}
//
// Implementation
//

View File

@ -271,8 +271,10 @@ public class MavenSession
{
this.projectMap = projectMap;
}
public Map<String, MavenProject> getProjectMap()
@Deprecated
/** @deprecated This appears to only be used in the ReactorReader and we can do any processing required there */
public Map<String, MavenProject> getProjectMap()
{
return projectMap;
}
@ -288,7 +290,7 @@ public class MavenSession
{
this.allProjects = allProjects;
}
/*if_not[MAVEN4]*/
//
@ -433,5 +435,4 @@ public class MavenSession
}
/*end[MAVEN4]*/
}

View File

@ -0,0 +1,487 @@
package org.apache.maven.graph;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.apache.maven.DefaultMaven;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.ProjectCycleException;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.building.DefaultModelProblem;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.ModelProblemUtils;
import org.apache.maven.model.building.ModelSource;
import org.apache.maven.model.building.Result;
import org.apache.maven.model.building.UrlModelSource;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.dag.CycleDetectedException;
import com.google.common.collect.Lists;
@Component( role = GraphBuilder.class, hint = GraphBuilder.HINT )
public class DefaultGraphBuilder
implements GraphBuilder
{
@Requirement
private Logger logger;
@Requirement
protected ProjectBuilder projectBuilder;
@Override
public Result<ProjectDependencyGraph> build( MavenSession session )
{
if ( session.getProjectDependencyGraph() != null )
{
return dependencyGraph( session, session.getProjects(), false );
}
List<MavenProject> projects = session.getProjects();
if ( projects == null )
{
try
{
projects = getProjectsForMavenReactor( session );
}
catch ( ProjectBuildingException e )
{
return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, e ) ) );
}
validateProjects( projects );
return dependencyGraph( session, projects, true );
}
else
{
return dependencyGraph( session, projects, false );
}
}
private Result<ProjectDependencyGraph> dependencyGraph( MavenSession session, List<MavenProject> projects,
boolean applyMakeBehaviour )
{
MavenExecutionRequest request = session.getRequest();
ProjectDependencyGraph projectDependencyGraph = null;
try
{
projectDependencyGraph = new DefaultProjectDependencyGraph( projects );
if ( applyMakeBehaviour )
{
List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects();
activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request );
activeProjects = trimExcludedProjects( activeProjects, request );
activeProjects = trimResumedProjects( activeProjects, request );
if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() )
{
projectDependencyGraph = new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects );
}
}
}
catch ( CycleDetectedException e )
{
String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
ProjectCycleException error = new ProjectCycleException( message, e );
return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, error ) ) );
}
catch ( org.apache.maven.project.DuplicateProjectException e )
{
return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, e ) ) );
}
catch ( MavenExecutionException e )
{
return Result.error( Lists.newArrayList( new DefaultModelProblem( null, null, null, null, 0, 0, e ) ) );
}
session.setProjects( projectDependencyGraph.getSortedProjects() );
session.setProjectDependencyGraph( projectDependencyGraph );
return Result.success( projectDependencyGraph );
}
private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
MavenExecutionRequest request )
throws MavenExecutionException
{
List<MavenProject> result = projects;
if ( !request.getSelectedProjects().isEmpty() )
{
File reactorDirectory = null;
if ( request.getBaseDirectory() != null )
{
reactorDirectory = new File( request.getBaseDirectory() );
}
Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() );
for ( String selector : request.getSelectedProjects() )
{
MavenProject selectedProject = null;
for ( MavenProject project : projects )
{
if ( isMatchingProject( project, selector, reactorDirectory ) )
{
selectedProject = project;
break;
}
}
if ( selectedProject != null )
{
selectedProjects.add( selectedProject );
}
else
{
throw new MavenExecutionException( "Could not find the selected project in the reactor: "
+ selector, request.getPom() );
}
}
boolean makeUpstream = false;
boolean makeDownstream = false;
if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
{
makeUpstream = true;
}
else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
{
makeDownstream = true;
}
else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
{
makeUpstream = true;
makeDownstream = true;
}
else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
{
throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
request.getPom() );
}
if ( makeUpstream || makeDownstream )
{
for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) )
{
if ( makeUpstream )
{
selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
}
if ( makeDownstream )
{
selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
}
}
}
result = new ArrayList<MavenProject>( selectedProjects.size() );
for ( MavenProject project : projects )
{
if ( selectedProjects.contains( project ) )
{
result.add( project );
}
}
}
return result;
}
private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request )
throws MavenExecutionException
{
List<MavenProject> result = projects;
if ( !request.getExcludedProjects().isEmpty() )
{
File reactorDirectory = null;
if ( request.getBaseDirectory() != null )
{
reactorDirectory = new File( request.getBaseDirectory() );
}
Collection<MavenProject> excludedProjects = new LinkedHashSet<MavenProject>( projects.size() );
for ( String selector : request.getExcludedProjects() )
{
MavenProject excludedProject = null;
for ( MavenProject project : projects )
{
if ( isMatchingProject( project, selector, reactorDirectory ) )
{
excludedProject = project;
break;
}
}
if ( excludedProject != null )
{
excludedProjects.add( excludedProject );
}
else
{
throw new MavenExecutionException( "Could not find the selected project in the reactor: "
+ selector, request.getPom() );
}
}
result = new ArrayList<MavenProject>( projects.size() );
for ( MavenProject project : projects )
{
if ( !excludedProjects.contains( project ) )
{
result.add( project );
}
}
}
return result;
}
private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
throws MavenExecutionException
{
List<MavenProject> result = projects;
if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
{
File reactorDirectory = null;
if ( request.getBaseDirectory() != null )
{
reactorDirectory = new File( request.getBaseDirectory() );
}
String selector = request.getResumeFrom();
result = new ArrayList<MavenProject>( projects.size() );
boolean resumed = false;
for ( MavenProject project : projects )
{
if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
{
resumed = true;
}
if ( resumed )
{
result.add( project );
}
}
if ( !resumed )
{
throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
+ " vs " + projects, request.getPom() );
}
}
return result;
}
private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
{
// [groupId]:artifactId
if ( selector.indexOf( ':' ) >= 0 )
{
String id = ':' + project.getArtifactId();
if ( id.equals( selector ) )
{
return true;
}
id = project.getGroupId() + id;
if ( id.equals( selector ) )
{
return true;
}
}
// relative path, e.g. "sub", "../sub" or "."
else if ( reactorDirectory != null )
{
File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
if ( selectedProject.isFile() )
{
return selectedProject.equals( project.getFile() );
}
else if ( selectedProject.isDirectory() )
{
return selectedProject.equals( project.getBasedir() );
}
}
return false;
}
private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
{
if ( !result.getExceptions().contains( e ) )
{
result.addException( e );
}
return result;
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Project collection
//
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private List<MavenProject> getProjectsForMavenReactor( MavenSession session )
throws ProjectBuildingException
{
MavenExecutionRequest request = session.getRequest();
request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
List<MavenProject> projects = new ArrayList<MavenProject>();
// We have no POM file.
//
if ( request.getPom() == null )
{
ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) );
MavenProject project = projectBuilder.build( modelSource, request.getProjectBuildingRequest() )
.getProject();
project.setExecutionRoot( true );
projects.add( project );
request.setProjectPresent( false );
return projects;
}
List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() );
collectProjects( projects, files, request );
return projects;
}
private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
throws ProjectBuildingException
{
ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
List<ProjectBuildingResult> results = projectBuilder.build( files, request.isRecursive(),
projectBuildingRequest );
boolean problems = false;
for ( ProjectBuildingResult result : results )
{
projects.add( result.getProject() );
if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
{
logger.warn( "" );
logger.warn( "Some problems were encountered while building the effective model for "
+ result.getProject().getId() );
for ( ModelProblem problem : result.getProblems() )
{
String loc = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( loc ) ? " @ " + loc : "" ) );
}
problems = true;
}
}
if ( problems )
{
logger.warn( "" );
logger.warn( "It is highly recommended to fix these problems"
+ " because they threaten the stability of your build." );
logger.warn( "" );
logger.warn( "For this reason, future Maven versions might no"
+ " longer support building such malformed projects." );
logger.warn( "" );
}
}
private void validateProjects( List<MavenProject> projects )
{
Map<String, MavenProject> projectsMap = new HashMap<String, MavenProject>();
for ( MavenProject p : projects )
{
String projectKey = ArtifactUtils.key( p.getGroupId(), p.getArtifactId(), p.getVersion() );
projectsMap.put( projectKey, p );
}
for ( MavenProject project : projects )
{
// MNG-1911 / MNG-5572: Building plugins with extensions cannot be part of reactor
for ( Plugin plugin : project.getBuildPlugins() )
{
if ( plugin.isExtensions() )
{
String pluginKey = ArtifactUtils.key( plugin.getGroupId(), plugin.getArtifactId(),
plugin.getVersion() );
if ( projectsMap.containsKey( pluginKey ) )
{
logger.warn( project.getName() + " uses " + plugin.getKey()
+ " as extensions, which is not possible within the same reactor build. "
+ "This plugin was pulled from the local repository!" );
}
}
}
}
}
}

View File

@ -1,4 +1,4 @@
package org.apache.maven;
package org.apache.maven.graph;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@ -36,7 +36,7 @@ import org.codehaus.plexus.util.dag.CycleDetectedException;
*
* @author Benjamin Bentmann
*/
class DefaultProjectDependencyGraph
public class DefaultProjectDependencyGraph
implements ProjectDependencyGraph
{

View File

@ -1,4 +1,4 @@
package org.apache.maven;
package org.apache.maven.graph;
/*
* Licensed to the Apache Software Foundation (ASF) under one

View File

@ -0,0 +1,31 @@
package org.apache.maven.graph;
/*
* 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 org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.model.building.Result;
public interface GraphBuilder
{
String HINT = "graphBuilder";
Result<? extends ProjectDependencyGraph> build( MavenSession session );
}

View File

@ -37,7 +37,7 @@ import org.apache.maven.plugin.version.PluginVersionResolutionException;
*
* @author Benjamin Bentmann
*/
class DefaultModelBuildingListener
public class DefaultModelBuildingListener
extends AbstractModelBuildingListener
{

View File

@ -1171,7 +1171,7 @@ public class MavenProject
return clone;
}
protected void setModel( Model model )
public void setModel( Model model )
{
this.model = model;
}

View File

@ -28,6 +28,7 @@ import java.util.Set;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Repository;
import org.apache.maven.model.building.FileModelSource;
@ -55,7 +56,7 @@ import org.eclipse.aether.resolution.VersionRangeResult;
*
* @author Benjamin Bentmann
*/
class ProjectModelResolver
public class ProjectModelResolver
implements ModelResolver
{

View File

@ -12,7 +12,7 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.maven;
package org.apache.maven.graph;
import junit.framework.TestCase;
import org.apache.maven.execution.ProjectDependencyGraph;

View File

@ -409,6 +409,11 @@ public class LifecycleExecutorTest
{
return Collections.emptyList();
}
public java.util.List<MavenProject> getAllSortedProjects()
{
return Collections.emptyList();
}
} );
final List<String> log = new ArrayList<String>();

View File

@ -51,7 +51,10 @@
<groupId>org.apache.maven</groupId>
<artifactId>maven-builder-support</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.sisu</groupId>
<artifactId>org.eclipse.sisu.plexus</artifactId>

View File

@ -19,6 +19,10 @@ package org.apache.maven.model.building;
* under the License.
*/
import static org.apache.maven.model.building.Result.error;
import static org.apache.maven.model.building.Result.newResult;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -63,6 +67,7 @@ import org.apache.maven.model.profile.ProfileSelector;
import org.apache.maven.model.resolution.InvalidRepositoryException;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.model.resolution.UnresolvableModelException;
import org.apache.maven.model.resolution.WorkspaceModelResolver;
import org.apache.maven.model.superpom.SuperPomProvider;
import org.apache.maven.model.validation.ModelValidator;
import org.codehaus.plexus.component.annotations.Component;
@ -241,8 +246,8 @@ public class DefaultModelBuilder
DefaultProfileActivationContext profileActivationContext = getProfileActivationContext( request );
problems.setSource( "(external profiles)" );
List<Profile> activeExternalProfiles =
profileSelector.getActiveProfiles( request.getProfiles(), profileActivationContext, problems );
List<Profile> activeExternalProfiles = profileSelector.getActiveProfiles( request.getProfiles(),
profileActivationContext, problems );
result.setActiveExternalProfiles( activeExternalProfiles );
@ -258,7 +263,11 @@ public class DefaultModelBuilder
}
// read and validate raw model
Model inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems );
Model inputModel = request.getRawModel();
if ( inputModel == null )
{
inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems );
}
problems.setRootModel( inputModel );
@ -272,11 +281,12 @@ public class DefaultModelBuilder
{
lineage.add( currentData );
Model tmpModel = currentData.getModel();
Model rawModel = tmpModel.clone();
Model rawModel = currentData.getModel();
currentData.setRawModel( rawModel );
Model tmpModel = rawModel.clone();
currentData.setModel( tmpModel );
problems.setSource( tmpModel );
// model normalization
@ -284,8 +294,8 @@ public class DefaultModelBuilder
profileActivationContext.setProjectProperties( tmpModel.getProperties() );
List<Profile> activePomProfiles =
profileSelector.getActiveProfiles( rawModel.getProfiles(), profileActivationContext, problems );
List<Profile> activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(),
profileActivationContext, problems );
currentData.setActiveProfiles( activePomProfiles );
Map<String, Activation> interpolatedActivations = getProfileActivations( rawModel, false );
@ -320,13 +330,13 @@ public class DefaultModelBuilder
}
else if ( currentData == resultData )
{ // First iteration - add initial parent id after version resolution.
currentData.setGroupId( currentData.getRawModel().getGroupId() == null
? parentData.getGroupId()
: currentData.getRawModel().getGroupId() );
currentData.setGroupId( currentData.getRawModel().getGroupId() == null ? parentData.getGroupId()
: currentData.getRawModel()
.getGroupId() );
currentData.setVersion( currentData.getRawModel().getVersion() == null
? parentData.getVersion()
: currentData.getRawModel().getVersion() );
currentData.setVersion( currentData.getRawModel().getVersion() == null ? parentData.getVersion()
: currentData.getRawModel()
.getVersion() );
currentData.setArtifactId( currentData.getRawModel().getArtifactId() );
parentIds.add( currentData.getId() );
@ -345,9 +355,8 @@ public class DefaultModelBuilder
}
message += parentData.getId();
problems.add(
new ModelProblemCollectorRequest( ModelProblem.Severity.FATAL, ModelProblem.Version.BASE ).
setMessage( message ) );
problems.add( new ModelProblemCollectorRequest( ModelProblem.Severity.FATAL, ModelProblem.Version.BASE )
.setMessage( message ) );
throw problems.newModelBuildingException();
}
@ -376,7 +385,7 @@ public class DefaultModelBuilder
modelUrlNormalizer.normalize( resultModel, request );
// Now the fully interpolated model is available: reconfigure the resolver
configureResolver( request.getModelResolver(), resultModel, problems , true );
configureResolver( request.getModelResolver(), resultModel, problems, true );
resultData.setGroupId( resultModel.getGroupId() );
resultData.setArtifactId( resultModel.getArtifactId() );
@ -469,6 +478,23 @@ public class DefaultModelBuilder
return result;
}
@Override
public Result<? extends Model> buildRawModel( File pomFile, int validationLevel, boolean locationTracking )
{
final ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel( validationLevel )
.setLocationTracking( locationTracking );
final DefaultModelProblemCollector collector =
new DefaultModelProblemCollector( new DefaultModelBuildingResult() );
try
{
return newResult( readModel( null, pomFile, request, collector ), collector.getProblems() );
}
catch ( ModelBuildingException e )
{
return error( collector.getProblems() );
}
}
private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingRequest request,
DefaultModelProblemCollector problems )
throws ModelBuildingException
@ -524,14 +550,14 @@ public class DefaultModelBuilder
if ( pomFile != null )
{
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.V20 )
.setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() )
.setException( e ) );
.setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() )
.setException( e ) );
}
else
{
problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 )
.setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() )
.setException( e ) );
.setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() )
.setException( e ) );
}
}
@ -544,8 +570,8 @@ public class DefaultModelBuilder
catch ( ModelParseException e )
{
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
.setMessage( "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage() )
.setException( e ) );
.setMessage( "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage() )
.setException( e ) );
throw problems.newModelBuildingException();
}
catch ( IOException e )
@ -564,8 +590,7 @@ public class DefaultModelBuilder
}
}
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
.setMessage( "Non-readable POM " + modelSource.getLocation() + ": " + msg )
.setException( e ) );
.setMessage( "Non-readable POM " + modelSource.getLocation() + ": " + msg ).setException( e ) );
throw problems.newModelBuildingException();
}
@ -621,9 +646,8 @@ public class DefaultModelBuilder
catch ( InvalidRepositoryException e )
{
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() )
.setLocation( repository.getLocation( "" ) )
.setException( e ) );
.setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() )
.setLocation( repository.getLocation( "" ) ).setException( e ) );
}
}
}
@ -675,7 +699,8 @@ public class DefaultModelBuilder
if ( versions.get( key ) == null && managedVersions.get( key ) == null )
{
InputLocation location = plugins.get( key ).getLocation( "" );
problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 )
problems
.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 )
.setMessage( "'build.plugins.plugin.version' for " + key + " is missing." )
.setLocation( location ) );
}
@ -800,9 +825,9 @@ public class DefaultModelBuilder
if ( !"pom".equals( parentModel.getPackaging() ) )
{
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel )
.setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel )
+ ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"" )
.setLocation( parentModel.getLocation( "packaging" ) ) );
.setLocation( parentModel.getLocation( "packaging" ) ) );
}
}
else
@ -817,20 +842,52 @@ public class DefaultModelBuilder
DefaultModelProblemCollector problems )
throws ModelBuildingException
{
ModelSource candidateSource = getParentPomFile( childModel, childSource );
if ( candidateSource == null )
final Parent parent = childModel.getParent();
final ModelSource candidateSource;
final Model candidateModel;
final WorkspaceModelResolver resolver = request.getWorkspaceModelResolver();
if ( resolver == null )
{
return null;
candidateSource = getParentPomFile( childModel, childSource );
if ( candidateSource == null )
{
return null;
}
File pomFile = null;
if ( candidateSource instanceof FileModelSource )
{
pomFile = ( (FileModelSource) candidateSource ).getPomFile();
}
candidateModel = readModel( candidateSource, pomFile, request, problems );
}
else
{
try
{
candidateModel =
resolver.resolveRawModel( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
}
catch ( UnresolvableModelException e )
{
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) //
.setMessage( e.getMessage().toString() ).setLocation( parent.getLocation( "" ) ).setException( e ) );
throw problems.newModelBuildingException();
}
if ( candidateModel == null )
{
return null;
}
candidateSource = new FileModelSource( candidateModel.getPomFile() );
}
File pomFile = null;
if ( candidateSource instanceof FileModelSource )
{
pomFile = ( (FileModelSource) candidateSource ).getPomFile();
}
Model candidateModel = readModel( candidateSource, pomFile, request, problems );
//
// TODO:jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we
// have a model that is suitable, yet more checks are done here and the one for the version is problematic
// before because with parents as ranges it will never work in this scenario.
//
String groupId = candidateModel.getGroupId();
if ( groupId == null && candidateModel.getParent() != null )
@ -844,8 +901,6 @@ public class DefaultModelBuilder
version = candidateModel.getParent().getVersion();
}
Parent parent = childModel.getParent();
if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null
|| !artifactId.equals( parent.getArtifactId() ) )
{
@ -861,15 +916,19 @@ public class DefaultModelBuilder
problems.setSource( childModel );
problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE )
.setMessage( buffer.toString() )
.setLocation( parent.getLocation( "" ) ) );
return null;
}
if ( version == null || !version.equals( parent.getVersion() ) )
{
.setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ) );
return null;
}
//
// Here we just need to know that a version is fine to use but this validation we can do in our workspace
// resolver.
//
/*
* if ( version == null || !version.equals( parent.getVersion() ) ) { return null; }
*/
ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version );
return parentData;
@ -944,9 +1003,7 @@ public class DefaultModelBuilder
}
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
.setMessage( buffer.toString() )
.setLocation( parent.getLocation( "" ) )
.setException( e ) );
.setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ).setException( e ) );
throw problems.newModelBuildingException();
}
@ -969,18 +1026,17 @@ public class DefaultModelBuilder
{
if ( childModel.getVersion() == null )
{
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ).
setMessage( "Version must be a constant" ).
setLocation( childModel.getLocation( "" ) ) );
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
.setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) );
}
else
{
if ( childModel.getVersion().indexOf( "${" ) > -1 )
{
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ).
setMessage( "Version must be a constant" ).
setLocation( childModel.getLocation( "version" ) ) );
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
.setMessage( "Version must be a constant" )
.setLocation( childModel.getLocation( "version" ) ) );
}
}
@ -1013,7 +1069,8 @@ public class DefaultModelBuilder
importIds.add( importing );
ModelResolver modelResolver = request.getModelResolver();
final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver();
final ModelResolver modelResolver = request.getModelResolver();
ModelBuildingRequest importRequest = null;
@ -1037,25 +1094,25 @@ public class DefaultModelBuilder
if ( groupId == null || groupId.length() <= 0 )
{
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( "'dependencyManagement.dependencies.dependency.groupId' for "
+ dependency.getManagementKey() + " is missing." )
.setLocation( dependency.getLocation( "" ) ) );
.setMessage( "'dependencyManagement.dependencies.dependency.groupId' for "
+ dependency.getManagementKey() + " is missing." )
.setLocation( dependency.getLocation( "" ) ) );
continue;
}
if ( artifactId == null || artifactId.length() <= 0 )
{
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for "
+ dependency.getManagementKey() + " is missing." )
.setLocation( dependency.getLocation( "" ) ) );
.setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for "
+ dependency.getManagementKey() + " is missing." )
.setLocation( dependency.getLocation( "" ) ) );
continue;
}
if ( version == null || version.length() <= 0 )
{
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( "'dependencyManagement.dependencies.dependency.version' for "
+ dependency.getManagementKey() + " is missing." )
.setLocation( dependency.getLocation( "" ) ) );
.setMessage( "'dependencyManagement.dependencies.dependency.version' for "
+ dependency.getManagementKey() + " is missing." )
.setLocation( dependency.getLocation( "" ) ) );
continue;
}
@ -1074,68 +1131,86 @@ public class DefaultModelBuilder
continue;
}
DependencyManagement importMngt =
getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT );
DependencyManagement importMngt = getCache( request.getModelCache(), groupId, artifactId, version,
ModelCacheTag.IMPORT );
if ( importMngt == null )
{
if ( modelResolver == null )
if ( workspaceResolver == null && modelResolver == null )
{
throw new IllegalArgumentException( "no model resolver provided, cannot resolve import POM "
+ ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM "
+ ModelProblemUtils.toSourceHint( model ) );
}
ModelSource importSource;
try
Model importModel = null;
if ( workspaceResolver != null )
{
importSource = modelResolver.resolveModel( groupId, artifactId, version );
}
catch ( UnresolvableModelException e )
{
StringBuilder buffer = new StringBuilder( 256 );
buffer.append( "Non-resolvable import POM" );
if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
try
{
buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
importModel = workspaceResolver.resolveEffectiveModel( groupId, artifactId, version );
}
buffer.append( ": " ).append( e.getMessage() );
catch ( UnresolvableModelException e )
{
problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
.setMessage( e.getMessage().toString() ).setException( e ) );
continue;
}
}
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( buffer.toString() )
.setLocation( dependency.getLocation( "" ) )
// no workspace resolver or workspace resolver returned null (i.e. model not in workspace)
if ( importModel == null )
{
final ModelSource importSource;
try
{
importSource = modelResolver.resolveModel( groupId, artifactId, version );
}
catch ( UnresolvableModelException e )
{
StringBuilder buffer = new StringBuilder( 256 );
buffer.append( "Non-resolvable import POM" );
if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
{
buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
}
buffer.append( ": " ).append( e.getMessage() );
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
.setMessage( buffer.toString() ).setLocation( dependency.getLocation( "" ) )
.setException( e ) );
continue;
continue;
}
if ( importRequest == null )
{
importRequest = new DefaultModelBuildingRequest();
importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
importRequest.setModelCache( request.getModelCache() );
importRequest.setSystemProperties( request.getSystemProperties() );
importRequest.setUserProperties( request.getUserProperties() );
importRequest.setLocationTracking( request.isLocationTracking() );
}
importRequest.setModelSource( importSource );
importRequest.setModelResolver( modelResolver.newCopy() );
final ModelBuildingResult importResult;
try
{
importResult = build( importRequest );
}
catch ( ModelBuildingException e )
{
problems.addAll( e.getProblems() );
continue;
}
problems.addAll( importResult.getProblems() );
importModel = importResult.getEffectiveModel();
}
if ( importRequest == null )
{
importRequest = new DefaultModelBuildingRequest();
importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
importRequest.setModelCache( request.getModelCache() );
importRequest.setSystemProperties( request.getSystemProperties() );
importRequest.setUserProperties( request.getUserProperties() );
importRequest.setLocationTracking( request.isLocationTracking() );
}
importRequest.setModelSource( importSource );
importRequest.setModelResolver( modelResolver.newCopy() );
ModelBuildingResult importResult;
try
{
importResult = build( importRequest );
}
catch ( ModelBuildingException e )
{
problems.addAll( e.getProblems() );
continue;
}
problems.addAll( importResult.getProblems() );
Model importModel = importResult.getEffectiveModel();
importMngt = importModel.getDependencyManagement();
if ( importMngt == null )

View File

@ -25,8 +25,10 @@ import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.model.resolution.WorkspaceModelResolver;
/**
* Collects settings that control building of effective models.
@ -37,6 +39,8 @@ public class DefaultModelBuildingRequest
implements ModelBuildingRequest
{
private Model rawModel;
private File pomFile;
private ModelSource modelSource;
@ -67,6 +71,8 @@ public class DefaultModelBuildingRequest
private ModelCache modelCache;
private WorkspaceModelResolver workspaceResolver;
/**
* Creates an empty request.
*/
@ -373,4 +379,30 @@ public class DefaultModelBuildingRequest
return this;
}
@Override
public Model getRawModel()
{
return rawModel;
}
@Override
public ModelBuildingRequest setRawModel( Model rawModel )
{
this.rawModel = rawModel;
return this;
}
@Override
public WorkspaceModelResolver getWorkspaceModelResolver()
{
return workspaceResolver;
}
@Override
public ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver workspaceResolver )
{
this.workspaceResolver = workspaceResolver;
return this;
}
}

View File

@ -24,8 +24,10 @@ import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.model.resolution.WorkspaceModelResolver;
/**
* A model building request that delegates all methods invocations to another request, meant for easy transformations by
@ -254,4 +256,30 @@ class FilterModelBuildingRequest
return this;
}
}
@Override
public Model getRawModel()
{
return request.getRawModel();
}
@Override
public ModelBuildingRequest setRawModel( Model rawModel )
{
request.setRawModel( rawModel );
return this;
}
@Override
public WorkspaceModelResolver getWorkspaceModelResolver()
{
return request.getWorkspaceModelResolver();
}
@Override
public ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver workspaceResolver )
{
request.setWorkspaceModelResolver( workspaceResolver );
return this;
}
}

View File

@ -19,6 +19,10 @@ package org.apache.maven.model.building;
* under the License.
*/
import java.io.File;
import org.apache.maven.model.Model;
/**
* Builds the effective model from a POM.
*
@ -51,4 +55,13 @@ public interface ModelBuilder
ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result )
throws ModelBuildingException;
/**
* Performs only the part of {@link ModelBuilder#build(ModelBuildingRequest)} that loads the raw model
*
* @param request
* @return
* @throws ModelBuildingException
*/
Result<? extends Model> buildRawModel( File pomFile, int validationLevel, boolean locationTracking );
}

View File

@ -24,8 +24,10 @@ import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.model.resolution.WorkspaceModelResolver;
/**
* Collects settings that control the building of effective models.
@ -62,6 +64,20 @@ public interface ModelBuildingRequest
*/
int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_3_0;
/**
* Gets the raw model to build. If not set, model source will be used to load raw model.
*
* @return The raw model to build or {@code null} if not set.
*/
Model getRawModel();
/**
* Set raw model.
*
* @param model
*/
ModelBuildingRequest setRawModel( Model rawModel );
/**
* Gets the source of the POM to process.
*
@ -315,4 +331,8 @@ public interface ModelBuildingRequest
*/
ModelBuildingRequest setModelCache( ModelCache modelCache );
}
WorkspaceModelResolver getWorkspaceModelResolver();
ModelBuildingRequest setWorkspaceModelResolver( WorkspaceModelResolver workspaceResolver );
}

View File

@ -0,0 +1,255 @@
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 static com.google.common.base.Predicates.in;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.transform;
import static java.util.Collections.singleton;
import static java.util.EnumSet.of;
import static org.apache.maven.model.building.ModelProblem.Severity.ERROR;
import static org.apache.maven.model.building.ModelProblem.Severity.FATAL;
import java.util.Arrays;
import java.util.Collections;
import org.apache.maven.model.building.ModelProblem.Severity;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
/**
* There are various forms of results that are represented by this class:
* <ol>
* <li>success - in which case only the model field is set
* <li>success with warnings - model field + non-error model problems
* <li>error - no model, but diagnostics
* <li>error - (partial) model and diagnostics
* </ol>
* Could encode these variants as subclasses, but kept in one for now
*
* @author bbusjaeger
* @param <T>
*/
public class Result<T>
{
/**
* Success without warnings
*
* @param model
* @return
*/
public static <T> Result<T> success( T model )
{
return success( model, Collections.<ModelProblem>emptyList() );
}
/**
* Success with warnings
*
* @param model
* @param problems
* @return
*/
public static <T> Result<T> success( T model, Iterable<? extends ModelProblem> problems )
{
assert !hasErrors( problems );
return new Result<T>( false, model, problems );
}
/**
* Success with warnings
*
* @param model
* @param results
* @return
*/
public static <T> Result<T> success( T model, Result<?>... results )
{
return success( model, Iterables.concat( Iterables.transform( Arrays.asList( results ), GET_PROBLEMS ) ) );
}
/**
* Error with problems describing the cause
*
* @param problems
* @return
*/
public static <T> Result<T> error( Iterable<? extends ModelProblem> problems )
{
return error( null, problems );
}
public static <T> Result<T> error( T model )
{
return error( model, Collections.<ModelProblem>emptyList() );
}
public static <T> Result<T> error( Result<?> result )
{
return error( result.getProblems() );
}
public static <T> Result<T> error( Result<?>... results )
{
return error( Iterables.concat( Iterables.transform( Arrays.asList( results ), GET_PROBLEMS ) ) );
}
/**
* Error with partial result and problems describing the cause
*
* @param model
* @param problems
* @return
*/
public static <T> Result<T> error( T model, Iterable<? extends ModelProblem> problems )
{
return new Result<T>( true, model, problems );
}
/**
* New result - determine whether error or success by checking problems for errors
*
* @param model
* @param problems
* @return
*/
public static <T> Result<T> newResult( T model, Iterable<? extends ModelProblem> problems )
{
return new Result<T>( hasErrors( problems ), model, problems );
}
/**
* New result consisting of given result and new problem. Convenience for newResult(result.get(),
* concat(result.getProblems(),problems)).
*
* @param result
* @param problem
* @return
*/
public static <T> Result<T> addProblem( Result<T> result, ModelProblem problem )
{
return addProblems( result, singleton( problem ) );
}
/**
* New result that includes the given
*
* @param result
* @param problems
* @return
*/
public static <T> Result<T> addProblems( Result<T> result, Iterable<? extends ModelProblem> problems )
{
return new Result<T>( result.hasErrors() || hasErrors( problems ), result.get(), concat( result.getProblems(),
problems ) );
}
public static <T> Result<T> addProblems( Result<T> result, Result<?>... results )
{
return addProblems( result, Iterables.concat( Iterables.transform( Arrays.asList( results ), GET_PROBLEMS ) ) );
}
/**
* Turns the given results into a single result by combining problems and models into single collection.
*
* @param results
* @return
*/
public static <T> Result<Iterable<T>> newResultSet( Iterable<? extends Result<? extends T>> results )
{
final boolean hasErrors = any( transform( results, new Function<Result<?>, Boolean>()
{
@Override
public Boolean apply( Result<?> input )
{
return input.hasErrors();
}
} ), Predicates.equalTo( true ) );
final Iterable<T> models = transform( results, new Function<Result<? extends T>, T>()
{
@Override
public T apply( Result<? extends T> input )
{
return input.get();
}
} );
final Iterable<ModelProblem> problems = concat( transform( results, GET_PROBLEMS ) );
return new Result<Iterable<T>>( hasErrors, models, problems );
}
// helper to determine if problems contain error
private static boolean hasErrors( Iterable<? extends ModelProblem> problems )
{
return any( transform( problems, new Function<ModelProblem, Severity>()
{
@Override
public Severity apply( ModelProblem input )
{
return input.getSeverity();
}
} ), in( of( ERROR, FATAL ) ) );
}
/**
* Class definition
*/
private final boolean errors;
private final T value;
private final Iterable<? extends ModelProblem> problems;
private Result( boolean errors, T model, Iterable<? extends ModelProblem> problems )
{
this.errors = errors;
this.value = model;
this.problems = problems;
}
public Iterable<? extends ModelProblem> getProblems()
{
return problems;
}
public T get()
{
return value;
}
public boolean hasErrors()
{
return errors;
}
private static final Function<Result<?>, Iterable<? extends ModelProblem>> GET_PROBLEMS =
new Function<Result<?>, Iterable<? extends ModelProblem>>()
{
@Override
public Iterable<? extends ModelProblem> apply( Result<?> input )
{
return input.getProblems();
}
};
}

View File

@ -77,6 +77,22 @@ public class UnresolvableModelException
this.version = ( version != null ) ? version : "";
}
/**
* Creates a new exception with specified cause
*
* @param cause
* @param groupId
* @param artifactId
* @param version
*/
public UnresolvableModelException( Throwable cause, String groupId, String artifactId, String version )
{
super( cause );
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
}
/**
* Gets the group id of the unresolvable model.
*

View File

@ -0,0 +1,33 @@
package org.apache.maven.model.resolution;
/*
* 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 org.apache.maven.model.Model;
public interface WorkspaceModelResolver
{
Model resolveRawModel( String groupId, String artifactId, String versionConstraint )
throws UnresolvableModelException;
Model resolveEffectiveModel( String groupId, String artifactId, String versionConstraint )
throws UnresolvableModelException;
}

View File

@ -1466,12 +1466,18 @@
<version>4.0.0+</version>
<code>
<![CDATA[
private String managementKey;
/**
* @return the management key as <code>groupId:artifactId:type</code>
*/
public String getManagementKey()
{
return groupId + ":" + artifactId + ":" + type + ( classifier != null ? ":" + classifier : "" );
if ( managementKey == null )
{
managementKey = groupId + ":" + artifactId + ":" + type + ( classifier != null ? ":" + classifier : "" );
}
return managementKey;
}
]]>
</code>