diff --git a/maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java b/maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java index 47155653dd..b2e536bfc2 100644 --- a/maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java +++ b/maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java @@ -154,9 +154,9 @@ public class DefaultMavenProjectBuilder private ModelValidator validator; - private Map rawProjectCache = new HashMap(); - private Map processedProjectCache = new HashMap(); + + private Map cachedPomFilesByModelId = new HashMap(); // TODO: make it a component private MavenXpp3Reader modelReader; @@ -683,8 +683,6 @@ public class DefaultMavenProjectBuilder } project.setOriginalModel( originalModel ); - - rawProjectCache.put( createCacheKey( project.getGroupId(), project.getArtifactId(), project.getVersion() ), new MavenProject( project ) ); // we don't have to force the collision exception for superModel here, it's already been done in getSuperModel() MavenProject previousProject = superProject; @@ -1023,7 +1021,7 @@ public class DefaultMavenProjectBuilder ModelLineage modelLineage = new DefaultModelLineage(); modelLineage.setOrigin( model, new File( projectDir, "pom.xml" ), new ArrayList( aggregatedRemoteWagonRepositories ) ); - modelLineageBuilder.resumeBuildingModelLineage( modelLineage, localRepository, externalProfileManager ); + modelLineageBuilder.resumeBuildingModelLineage( modelLineage, localRepository, externalProfileManager, cachedPomFilesByModelId ); List explicitlyActive; List explicitlyInactive; diff --git a/maven-project/src/main/java/org/apache/maven/project/build/model/DefaultModelLineageBuilder.java b/maven-project/src/main/java/org/apache/maven/project/build/model/DefaultModelLineageBuilder.java index cdff77b5ec..16fffa4635 100644 --- a/maven-project/src/main/java/org/apache/maven/project/build/model/DefaultModelLineageBuilder.java +++ b/maven-project/src/main/java/org/apache/maven/project/build/model/DefaultModelLineageBuilder.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -63,7 +64,7 @@ public class DefaultModelLineageBuilder * @see org.apache.maven.project.build.model.ModelLineageBuilder#buildModelLineage(java.io.File, org.apache.maven.artifact.repository.ArtifactRepository, java.util.List) */ public ModelLineage buildModelLineage( File pom, ArtifactRepository localRepository, List remoteRepositories, - ProfileManager profileManager ) + ProfileManager profileManager, Map cachedPomFilesByModelId ) throws ProjectBuildingException { ModelLineage lineage = new DefaultModelLineage(); @@ -74,7 +75,7 @@ public class DefaultModelLineageBuilder while ( pomFile != null ) { - Model model = readModel( pomFile ); + Model model = readModel( pomFile, cachedPomFilesByModelId ); if ( lineage.size() == 0 ) { @@ -87,14 +88,15 @@ public class DefaultModelLineageBuilder currentRemoteRepositories = updateRepositorySet( model, currentRemoteRepositories, pomFile, profileManager ); - pomFile = resolveParentPom( model, currentRemoteRepositories, localRepository, pomFile ); + pomFile = resolveParentPom( model, currentRemoteRepositories, localRepository, pomFile, + cachedPomFilesByModelId ); } return lineage; } public void resumeBuildingModelLineage( ModelLineage lineage, ArtifactRepository localRepository, - ProfileManager profileManager ) + ProfileManager profileManager, Map cachedPomFilesByModelId ) throws ProjectBuildingException { File pomFile = lineage.getDeepestFile(); @@ -108,11 +110,11 @@ public class DefaultModelLineageBuilder Model model = lineage.getDeepestModel(); // use the above information to re-bootstrap the resolution chain... - pomFile = resolveParentPom( model, currentRemoteRepositories, localRepository, pomFile ); + pomFile = resolveParentPom( model, currentRemoteRepositories, localRepository, pomFile, cachedPomFilesByModelId ); while ( pomFile != null ) { - model = readModel( pomFile ); + model = readModel( pomFile, cachedPomFilesByModelId ); if ( lineage.size() == 0 ) { @@ -125,15 +127,38 @@ public class DefaultModelLineageBuilder currentRemoteRepositories = updateRepositorySet( model, currentRemoteRepositories, pomFile, profileManager ); - pomFile = resolveParentPom( model, currentRemoteRepositories, localRepository, pomFile ); + pomFile = resolveParentPom( model, currentRemoteRepositories, localRepository, pomFile, + cachedPomFilesByModelId ); } } /** - * Read the Model instance from the given POM file. + * Read the Model instance from the given POM file. Skip caching the Model on this call, since + * it's meant for diagnostic purposes (to determine a parent match). */ private Model readModel( File pomFile ) throws ProjectBuildingException + { + return readModel( pomFile, null, true ); + } + + /** + * Read the Model instance from the given POM file, and cache it in the given Map before + * returning it. + */ + private Model readModel( File pomFile, Map cachedPomFilesByModelId ) + throws ProjectBuildingException + { + return readModel( pomFile, cachedPomFilesByModelId, false ); + } + + /** + * Read the Model instance from the given POM file. Optionally (in normal cases) cache the + * Model instance in the given Map before returning it. The skipCache flag controls whether the + * Model instance is actually cached. + */ + private Model readModel( File pomFile, Map cachedPomFilesByModelId, boolean skipCache ) + throws ProjectBuildingException { Model model; FileReader reader = null; @@ -156,6 +181,11 @@ public class DefaultModelLineageBuilder IOUtil.close( reader ); } + if ( !skipCache ) + { + cachedPomFilesByModelId.put( createCacheKey( model, pomFile ), pomFile ); + } + return model; } @@ -203,7 +233,7 @@ public class DefaultModelLineageBuilder { List explicitlyActive; List explicitlyInactive; - + if ( profileManager != null ) { explicitlyActive = profileManager.getExplicitlyActivatedIds(); @@ -214,8 +244,10 @@ public class DefaultModelLineageBuilder explicitlyActive = Collections.EMPTY_LIST; explicitlyInactive = Collections.EMPTY_LIST; } - - LinkedHashSet profileRepos = profileAdvisor.getArtifactRepositoriesFromActiveProfiles( model, projectDir, explicitlyActive, explicitlyInactive ); + + LinkedHashSet profileRepos = profileAdvisor.getArtifactRepositoriesFromActiveProfiles( model, projectDir, + explicitlyActive, + explicitlyInactive ); if ( !profileRepos.isEmpty() ) { @@ -226,9 +258,10 @@ public class DefaultModelLineageBuilder /** * Pull the parent specification out of the given model, construct an Artifact instance, and * resolve that artifact...then, return the resolved POM file for the parent. + * @param cachedModelsById */ private File resolveParentPom( Model model, List remoteRepositories, ArtifactRepository localRepository, - File modelPomFile ) + File modelPomFile, Map cachedModelsById ) throws ProjectBuildingException { Parent modelParent = model.getParent(); @@ -239,7 +272,16 @@ public class DefaultModelLineageBuilder { validateParentDeclaration( modelParent, model ); - pomFile = resolveParentWithRelativePath( modelParent, modelPomFile ); + String cacheKey = createCacheKey( modelParent ); + + getLogger().debug( "Looking for cached parent POM under: " + cacheKey ); + + pomFile = (File) cachedModelsById.get( cacheKey ); + + if ( pomFile == null ) + { + pomFile = resolveParentWithRelativePath( modelParent, modelPomFile ); + } if ( pomFile == null ) { @@ -250,6 +292,42 @@ public class DefaultModelLineageBuilder return pomFile; } + private String createCacheKey( Parent modelParent ) + { + return modelParent.getGroupId() + ":" + modelParent.getArtifactId() + ":" + modelParent.getVersion(); + } + + private String createCacheKey( Model model, File pomFile ) + throws ProjectBuildingException + { + Parent modelParent = model.getParent(); + + String groupId = model.getGroupId(); + + if ( groupId == null && modelParent != null ) + { + groupId = modelParent.getGroupId(); + } + + String artifactId = model.getArtifactId(); + + String version = model.getVersion(); + + if ( version == null && modelParent != null ) + { + version = modelParent.getVersion(); + } + + if ( groupId == null || version == null ) + { + throw new ProjectBuildingException( model.getId(), + "Invalid model. Must either specify groupId and version directly, or specify a parent groupId and version.\nIn POM: " + + pomFile ); + } + + return groupId + ":" + artifactId + ":" + version; + } + private void validateParentDeclaration( Parent modelParent, Model model ) throws ProjectBuildingException { @@ -283,7 +361,7 @@ public class DefaultModelLineageBuilder getLogger().debug( "Looking for parent: " + modelParent.getId() + " using artifact: " + parentPomArtifact ); getLogger().debug( "\tLocal repository: " + localRepository.getBasedir() + "\n" ); getLogger().debug( "\tRemote repositories:\n" + remoteRepositories.toString().replace( ',', '\n' ) + "\n" ); - + try { artifactResolver.resolve( parentPomArtifact, remoteRepositories, localRepository ); @@ -295,8 +373,8 @@ public class DefaultModelLineageBuilder } catch ( ArtifactNotFoundException e ) { - throw new ProjectBuildingException( "Parent: " + modelParent.getId(), "Cannot find parent: " + parentPomArtifact.getId() + " of: " - + pomFile, e ); + throw new ProjectBuildingException( "Parent: " + modelParent.getId(), "Cannot find parent: " + + parentPomArtifact.getId() + " of: " + pomFile, e ); } if ( parentPomArtifact.isResolved() ) @@ -316,13 +394,13 @@ public class DefaultModelLineageBuilder File modelDir = modelPomFile.getParentFile(); File parentPomFile = new File( modelDir, relativePath ); - + if ( parentPomFile.isDirectory() ) { getLogger().debug( "Parent relative-path is a directory; assuming \'pom.xml\' file exists within." ); parentPomFile = new File( parentPomFile, "pom.xml" ); } - + getLogger().debug( "Looking for parent: " + modelParent.getId() + " in: " + parentPomFile ); if ( parentPomFile.exists() ) @@ -349,7 +427,7 @@ public class DefaultModelLineageBuilder { logger = new ConsoleLogger( Logger.LEVEL_DEBUG, "DefaultModelLineageBuilder:internal" ); } - + return logger; } diff --git a/maven-project/src/main/java/org/apache/maven/project/build/model/ModelLineageBuilder.java b/maven-project/src/main/java/org/apache/maven/project/build/model/ModelLineageBuilder.java index e2f09bb7e9..9e5027a955 100644 --- a/maven-project/src/main/java/org/apache/maven/project/build/model/ModelLineageBuilder.java +++ b/maven-project/src/main/java/org/apache/maven/project/build/model/ModelLineageBuilder.java @@ -6,6 +6,7 @@ import org.apache.maven.project.ProjectBuildingException; import java.io.File; import java.util.List; +import java.util.Map; /** * Builds the lineage of Model instances, starting from a given POM file, and stretching back through @@ -29,16 +30,30 @@ public interface ModelLineageBuilder * @param localRepository The local repository against which parent POMs should be resolved * @param remoteRepositories List of ArtifactRepository instances against which parent POMs * should be resolved + * @param profileManager The profile manager containing information about global profiles to be + * applied (from settings.xml, for instance) + * @param cachedPomFilesByModelId A "global" cache of Model source files, partially for + * optimization, and partially for loading Models that are unavailable in the repository and + * have an incorrect relativePath */ - ModelLineage buildModelLineage( File pom, ArtifactRepository localRepository, List remoteRepositories, ProfileManager profileManager ) + ModelLineage buildModelLineage( File pom, ArtifactRepository localRepository, List remoteRepositories, + ProfileManager profileManager, Map cachedPomFilesByModelId ) throws ProjectBuildingException; - + /** * Resume the process of constructing a lineage of inherited models, picking up using the deepest * parent already in the lineage. * + * @param lineage The ModelLineage instance in progress, which should be completed. + * @param localRepository The local repository against which parent POMs should be resolved + * @param profileManager The profile manager containing information about global profiles to be + * applied (from settings.xml, for instance) + * @param cachedPomFilesByModelId A "global" cache of Model source files, partially for + * optimization, and partially for loading Models that are unavailable in the repository and + * have an incorrect relativePath */ - void resumeBuildingModelLineage( ModelLineage lineage, ArtifactRepository localRepository, ProfileManager profileManager ) + void resumeBuildingModelLineage( ModelLineage lineage, ArtifactRepository localRepository, + ProfileManager profileManager, Map cachedPomFilesByModelId ) throws ProjectBuildingException; } diff --git a/maven-project/src/test/java/org/apache/maven/project/build/model/DefaultModelLineageBuilderTest.java b/maven-project/src/test/java/org/apache/maven/project/build/model/DefaultModelLineageBuilderTest.java index 7850a2ae51..7120efac9f 100644 --- a/maven-project/src/test/java/org/apache/maven/project/build/model/DefaultModelLineageBuilderTest.java +++ b/maven-project/src/test/java/org/apache/maven/project/build/model/DefaultModelLineageBuilderTest.java @@ -16,7 +16,9 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; public class DefaultModelLineageBuilderTest extends PlexusTestCase @@ -66,7 +68,7 @@ public class DefaultModelLineageBuilderTest IOUtil.close( writer ); } - ModelLineage lineage = modelLineageBuilder.buildModelLineage( pomFile, null, null, null ); + ModelLineage lineage = modelLineageBuilder.buildModelLineage( pomFile, null, null, null, new HashMap() ); assertEquals( 1, lineage.size() ); @@ -124,7 +126,7 @@ public class DefaultModelLineageBuilderTest .toExternalForm(), defaultLayout ); ModelLineage lineage = modelLineageBuilder.buildModelLineage( currentPOM, localRepository, - Collections.EMPTY_LIST, null ); + Collections.EMPTY_LIST, null, new HashMap() ); assertEquals( 3, lineage.size() ); @@ -196,7 +198,7 @@ public class DefaultModelLineageBuilderTest .toExternalForm(), defaultLayout ); ModelLineage lineage = modelLineageBuilder.buildModelLineage( currentPOM, localRepository, Collections - .singletonList( remoteRepository ), null ); + .singletonList( remoteRepository ), null, new HashMap() ); assertEquals( 3, lineage.size() ); @@ -217,11 +219,11 @@ public class DefaultModelLineageBuilderTest projectRootDirectory.mkdirs(); deleteDirOnExit( projectRootDirectory ); - + // 2. create dir for parent POM within project root directory. File parentDir = new File( projectRootDirectory, "parent" ); parentDir.mkdirs(); - + // 2. create dir for child project within project root directory. File childDir = new File( projectRootDirectory, "child" ); childDir.mkdirs(); @@ -250,8 +252,8 @@ public class DefaultModelLineageBuilderTest ArtifactRepository localRepository = new DefaultArtifactRepository( "local", projectRootDirectory.toURL() .toExternalForm(), defaultLayout ); - ModelLineage lineage = modelLineageBuilder.buildModelLineage( currentPOM, localRepository, Collections - .EMPTY_LIST, null ); + ModelLineage lineage = modelLineageBuilder.buildModelLineage( currentPOM, localRepository, + Collections.EMPTY_LIST, null, new HashMap() ); assertEquals( 2, lineage.size() ); @@ -300,6 +302,51 @@ public class DefaultModelLineageBuilderTest System.out.println( "Verifying that: " + file.getAbsolutePath() + " exists: " + file.exists() ); } + public void testReadPOMWithParentInOtherLocalFileWithBadRelativePath() + throws IOException, ProjectBuildingException + { + // 1. create the parent model in a "local" POM file. + File parentPOM = File.createTempFile( "DefaultModelLineageBuilder.test.", ".pom" ); + parentPOM.deleteOnExit(); + + Model parent = createModel( "group", "parent", "1" ); + + // 4. write the parent model to the local repo directory + writeModel( parent, parentPOM ); + + Map cache = new HashMap(); + cache.put( "group:parent:1", parentPOM ); + + // 5. create the current pom with a parent-ref on the parent model + Model current = createModel( "group", "current", "1" ); + + Parent currentParent = new Parent(); + currentParent.setGroupId( "group" ); + currentParent.setArtifactId( "parent" ); + currentParent.setVersion( "1" ); + currentParent.setRelativePath( "../parent/pom.xml" ); + + current.setParent( currentParent ); + + // 6. write the current pom somewhere + File currentPOM = File.createTempFile( "DefaultModelLineageBuilder.test.", ".pom" ); + currentPOM.deleteOnExit(); + + writeModel( current, currentPOM ); + + // 7. build the lineage. + ModelLineage lineage = modelLineageBuilder.buildModelLineage( currentPOM, null, Collections + .EMPTY_LIST, null, cache ); + + assertEquals( 2, lineage.size() ); + + Iterator modelIterator = lineage.modelIterator(); + + assertEquals( 2, cache.size() ); + assertEquals( current.getId(), ( (Model) modelIterator.next() ).getId() ); + assertEquals( parent.getId(), ( (Model) modelIterator.next() ).getId() ); + } + private Model createModel( String groupId, String artifactId, String version ) { Model model = new Model();