Index: maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java =================================================================== --- maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java (revision 510454) +++ maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java (working copy) @@ -86,6 +86,8 @@ private Map metadataMap; private boolean optional; + + private String modelRepresentation; public DefaultArtifact( String groupId, String artifactId, VersionRange versionRange, String scope, String type, String classifier, ArtifactHandler artifactHandler ) @@ -584,4 +586,14 @@ { this.optional = optional; } + + public String getModelRepresentation() + { + return modelRepresentation; + } + + public void setModelRepresentation( String modelRepresentation ) + { + this.modelRepresentation = modelRepresentation; + } } Index: maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java =================================================================== --- maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java (revision 510454) +++ maven-artifact/src/main/java/org/apache/maven/artifact/Artifact.java (working copy) @@ -167,4 +167,9 @@ boolean isSelectedVersionKnown() throws OverConstrainedVersionException; + + // For MNG-624, we sometimes need a slightly modified model to install and deploy + String getModelRepresentation(); + + void setModelRepresentation( String modelRepresentation ); } \ No newline at end of file Index: maven-artifact-manager/src/main/java/org/apache/maven/artifact/deployer/DefaultArtifactDeployer.java =================================================================== --- maven-artifact-manager/src/main/java/org/apache/maven/artifact/deployer/DefaultArtifactDeployer.java (revision 510454) +++ maven-artifact-manager/src/main/java/org/apache/maven/artifact/deployer/DefaultArtifactDeployer.java (working copy) @@ -68,16 +68,30 @@ try { + // This updates the artifact and causes the artifactFile (below) location computation to likely change (especially for snapshots) transformationManager.transformForDeployment( artifact, deploymentRepository, localRepository ); - // Copy the original file to the new one if it was transformed + // A location in the local repository to stash our artifact before uploading File artifactFile = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); - if ( !artifactFile.equals( source ) ) + + if ( "pom".equals( artifact.getType() ) && artifact.getModelRepresentation() != null ) { - FileUtils.copyFile( source, artifactFile ); + // MNG-624: Need to use our expanded model that includes the proper entries + FileUtils.fileWrite( artifactFile.getAbsolutePath(), artifact.getModelRepresentation() ); + + // Need to put the artifactFile we just wrote, not the source + // Note: The source for a pom is the file you edit, don't want to change it! + wagonManager.putArtifact( artifactFile, artifact, deploymentRepository ); } + else + { + if ( !artifactFile.equals( source ) ) + { + FileUtils.copyFile( source, artifactFile ); + } - wagonManager.putArtifact( source, artifact, deploymentRepository ); + wagonManager.putArtifact( source, artifact, deploymentRepository ); + } // must be after the artifact is installed for ( Iterator i = artifact.getMetadataList().iterator(); i.hasNext(); ) Index: maven-artifact-manager/src/main/java/org/apache/maven/artifact/installer/DefaultArtifactInstaller.java =================================================================== --- maven-artifact-manager/src/main/java/org/apache/maven/artifact/installer/DefaultArtifactInstaller.java (revision 510454) +++ maven-artifact-manager/src/main/java/org/apache/maven/artifact/installer/DefaultArtifactInstaller.java (working copy) @@ -68,10 +68,20 @@ destination.getParentFile().mkdirs(); } - getLogger().info( "Installing " + source.getPath() + " to " + destination ); + if ( "pom".equals( artifact.getType() ) && artifact.getModelRepresentation() != null ) + { + getLogger().info( "Installing expanded " + source.getPath() + " to " + destination ); - FileUtils.copyFile( source, destination ); + // For MNG-624, we cannot always just copy the pom if part of the was inferred and computed + FileUtils.fileWrite( destination.getAbsolutePath(), artifact.getModelRepresentation() ); + } + else + { + getLogger().info( "Installing " + source.getPath() + " to " + destination ); + FileUtils.copyFile( source, destination ); + } + // must be after the artifact is installed for ( Iterator i = artifact.getMetadataList().iterator(); i.hasNext(); ) { Index: maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java =================================================================== --- maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java (revision 510454) +++ maven-project/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java (working copy) @@ -47,6 +47,7 @@ import org.apache.maven.model.ReportPlugin; import org.apache.maven.model.Repository; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.profiles.DefaultProfileManager; import org.apache.maven.profiles.MavenProfilesBuilder; import org.apache.maven.profiles.ProfileManager; @@ -115,6 +116,7 @@ * inheritance * interpolation + * validate inheritance (so variables and missing elements can be applied to ) * defaults injection * path translation @@ -131,6 +133,14 @@ */ /** + * Read in a model (pom.xml) from disk, local or remote repository and build a project from it. + * + * In the process of processing the model, parent-models will be found and relevant things extracted: + * variables, profiles, plugins, dependencies. + * + * Sub-modules are processed by L{org.apache.maven.DefaultMaven} which is generally what calls into + * this class regardless. + * * @version $Id: DefaultMavenProjectBuilder.java,v 1.37 2005/03/08 01:55:22 * trygvis Exp $ */ @@ -280,7 +290,8 @@ try { - project = processProjectLogic( "", project, null, null, true ); + project = expandProjectDefinition( project, null, null, true ); + processProjectLogic( "", project ); project.setExecutionRoot( true ); @@ -709,19 +720,20 @@ } project.setOriginalModel( originalModel ); - - rawProjectCache.put( createCacheKey( project.getGroupId(), project.getArtifactId(), project.getVersion() ), new MavenProject( project ) ); + MavenProject rawProject = new MavenProject( project ); // Non-interpolated read-only wo/parent + // we don't have to force the collision exception for superModel here, it's already been done in getSuperModel() MavenProject previousProject = superProject; - Model previous = superProject.getModel(); + Model previous = superProject.getModel(); // parent + // Using information from a project's lineage, fix any missing attributes in models (modifying them) for ( Iterator i = lineage.iterator(); i.hasNext(); ) { MavenProject currentProject = (MavenProject) i.next(); - Model current = currentProject.getModel(); + Model current = currentProject.getModel(); // child String pathAdjustment = null; @@ -757,19 +769,66 @@ try { - project = processProjectLogic( pomLocation, project, externalProfileManager, projectDir, strict ); + project = expandProjectDefinition( project, externalProfileManager, projectDir, strict ); + + // Fixup/validate parent if we had any variables in the version strings initially + validateImpliedInheritance( project, pomLocation, externalProfileManager, strict ); + + if ( originalModel.getParent() != null && !isParentIdExpanded( originalModel.getParent() ) ) + { + // If we had to fix the implied inheritance and in the process patch our version or group, we may need + // to expand again to ensure any thing like ${pom.version} in dependencies, etc. is properly expanded + project = expandProjectDefinition( project, externalProfileManager, projectDir, strict ); + } } catch ( ModelInterpolationException e ) { throw new InvalidProjectModelException( projectId, pomLocation, e.getMessage(), e ); } + + // Finish processing the project logic... + try + { + processProjectLogic( pomLocation, project ); + } catch ( InvalidRepositoryException e ) { throw new InvalidProjectModelException( projectId, pomLocation, e.getMessage(), e ); } - processedProjectCache.put( createCacheKey( project.getGroupId(), project.getArtifactId(), project.getVersion() ), project ); + + if ( ( originalModel.getParent() != null && !isParentIdExpanded( originalModel.getParent() ) ) ) // || !isModelIdExpanded( originalModel ) ) + { + // Write a modified data segment to the artifact + Model newModel = ModelUtils.cloneModel( originalModel ); + newModel.setParent( project.getModel().getParent() ); + newModel.setGroupId( project.getGroupId() ); + newModel.setVersion( project.getVersion() ); + + StringWriter sWriter = new StringWriter(); + MavenXpp3Writer writer = new MavenXpp3Writer(); + try + { + writer.write( sWriter, newModel ); + } + catch ( IOException e ) + { + throw new ProjectBuildingException( projectId, "Cannot serialize modified model", e ); + } + + sWriter.write( "\n\n" ); + + getLogger().debug( "New model:\n" + sWriter.toString() ); + + project.getArtifact().setModelRepresentation( sWriter.toString() ); + } + + String cacheKey = createCacheKey( project.getGroupId(), project.getArtifactId(), project.getVersion() ); + + rawProjectCache.put( cacheKey, rawProject ); // do it here after version's been expanded + processedProjectCache.put( cacheKey, project ); + // jvz:note // this only happens if we are building from a source file if ( projectDescriptor != null ) @@ -793,7 +852,7 @@ if ( rawParent != null ) { - String cacheKey = createCacheKey( rawParent.getGroupId(), rawParent.getArtifactId(), rawParent.getVersion() ); + cacheKey = createCacheKey( rawParent.getGroupId(), rawParent.getArtifactId(), rawParent.getVersion() ); MavenProject processedParent = (MavenProject) processedProjectCache.get( cacheKey ); @@ -806,7 +865,115 @@ return project; } + + /** + * If the project's parent definition or its actual parent had any variables in their definition or any missing + * details, assembleLineage() would have had to guess at who our parent was. This routine verifies who our parent + * really is and makes sure that missing details are filled-in and any variables are suitably expanded. + */ + private void validateImpliedInheritance(MavenProject project, String pomLocation, ProfileManager externalProfileManager, boolean strict) + throws ProjectBuildingException, InvalidProjectModelException + { + Parent projectModelParent = project.getModel().getParent(); + MavenProject candidateParentProject = project.getParent(); + if ( projectModelParent != null && candidateParentProject != null && candidateParentProject.getModel() != null ) + { + Model candidateParentModel = candidateParentProject.getOriginalModel(); + if ( candidateParentModel == null ) + { + candidateParentModel = project.getParent().getModel(); + } + + Parent originalProjectModelParent = null; + if ( project.getOriginalModel() != null ) + { + originalProjectModelParent = project.getOriginalModel().getParent(); + } + + String version = fixParentVersion( project ); + String groupId = fixParentGroupId( project ); + fixParentArtifactId( project ); + + if ( originalProjectModelParent != null && ( !isParentIdExpanded( originalProjectModelParent ) || !isModelIdExpanded( candidateParentModel ) ) ) + { + // Now that our model has been expanded (interpolated + profiles injected, etc.), parent-version will be expanded + String parentKey = createCacheKey( projectModelParent.getGroupId(), projectModelParent.getArtifactId(), projectModelParent.getVersion() ); + + // Try to get the processed (interpolated, etc.) parent + MavenProject parentProject = (MavenProject) processedProjectCache.get( parentKey ); + + if ( parentProject != null ) + { + // We have the parent, we just weren't able to match it before, but now we can + project.setParent( new MavenProject( parentProject ) ); // expanded read-only wo/parent + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Found " + project.getId() + "'s parent: " + parentProject.getId() ); + } + } + else + { + // We're probably running mvn below the parent, so we've probably just read the parent pom.xml in when building lineage + // Thus, we need to expand/interpolate the parent so we can properly compare versions to ensure they match + try + { + candidateParentProject = project.getParent(); // non-expanded + candidateParentProject = expandProjectDefinition( candidateParentProject, externalProfileManager, candidateParentProject.getFile(), strict ); + boolean reExpand = false; + + // It is quite possible that the candidate tried to inherit its version too... + if ( StringUtils.isEmpty( candidateParentProject.getVersion() ) ) + { + candidateParentProject.setVersion( version ); + reExpand = true; + } + + if ( StringUtils.isEmpty( candidateParentProject.getGroupId() ) ) + { + candidateParentProject.setGroupId( groupId ); + reExpand = true; + } + + if ( reExpand ) + { + candidateParentProject = expandProjectDefinition( candidateParentProject, externalProfileManager, candidateParentProject.getFile(), strict ); + } + } + catch ( ModelInterpolationException e ) + { + throw new ProjectBuildingException( project.getId(), "Unable to expand " + project.getId() + "'s pom.xml.", e ); + } + + // Now validate that everything matches + if ( !projectModelParent.getGroupId().equals( candidateParentProject.getGroupId() ) || + !projectModelParent.getArtifactId().equals( candidateParentProject.getArtifactId() ) || + !projectModelParent.getVersion().equals( candidateParentProject.getVersion() ) ) + { + throw new ProjectBuildingException( project.getId(), "Unable to find parent for " + project.getId() + + "\n Specified: " + projectModelParent.getId() + + "\n Found: " + candidateParentProject.getId() ); + } + + project.setParent( candidateParentProject ); + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Interpolated and matched " + project.getId() + "'s candidate parent: " + candidateParentProject.getId() ); + } + } + + if ( project.getGroupId().equals( project.getParent().getGroupId() ) && + project.getArtifactId().equals( project.getParent().getArtifactId() ) ) + { + throw new ProjectBuildingException( project.getId(), "Parent groupId/artifactId in '" + pomLocation + + "' is a duplicate of the current project" ); + } + } + } + } + private String safeVersionlessKey( String groupId, String artifactId ) { String gid = groupId; @@ -842,18 +1009,16 @@ } /** - * @todo can this take in a model instead of a project and still be successful? - * @todo In fact, does project REALLY need a MavenProject as a parent? Couldn't it have just a wrapper around a - * model that supported parents which were also the wrapper so that inheritence was assembled. We don't really need - * the resolved source roots, etc for the parent - that occurs for the parent when it is constructed independently - * and projects are not cached or reused + * Return a new project based on an interpolated model that's had the active profiles injected. + * + * @param project The unexpanded (original) maven project + * @param profileMgr The profile manager + * @param projectDir The directory for this maven project + * @param strict Should we be strict processing the pom.xml? + * @return A new project based on the expanded project/model definition */ - private MavenProject processProjectLogic( String pomLocation, - MavenProject project, - ProfileManager profileMgr, - File projectDir, - boolean strict ) - throws ProjectBuildingException, ModelInterpolationException, InvalidRepositoryException + private MavenProject expandProjectDefinition( MavenProject project, ProfileManager profileMgr, File projectDir, boolean strict ) + throws ProjectBuildingException, ModelInterpolationException { Model model = project.getModel(); @@ -904,11 +1069,38 @@ project.setActiveProfiles( activeProfiles ); + project.setParent( parentProject ); + + return project; + } + + /** + * @todo can this take in a model instead of a project and still be successful? + * @todo In fact, does project REALLY need a MavenProject as a parent? Couldn't it have just a wrapper around a + * model that supported parents which were also the wrapper so that inheritence was assembled. We don't really need + * the resolved source roots, etc for the parent - that occurs for the parent when it is constructed independently + * and projects are not cached or reused + */ + private void processProjectLogic( String pomLocation, MavenProject project ) + throws ProjectBuildingException, InvalidRepositoryException + { + MavenProject parentProject = project.getParent(); + // TODO: maybe not strictly correct, while we should enfore that packaging has a type handler of the same id, we don't Artifact projectArtifact = artifactFactory.createBuildArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), project.getPackaging() ); project.setArtifact( projectArtifact ); + if ( parentProject != null ) + { + Artifact parentArtifact = artifactFactory.createParentArtifact( parentProject.getGroupId(), + parentProject.getArtifactId(), + parentProject.getVersion() ); + project.setParentArtifact( parentArtifact ); + } + + Model model = project.getModel(); + project.setPluginArtifactRepositories( ProjectUtils.buildArtifactRepositories( model.getPluginRepositories(), artifactRepositoryFactory, container ) ); @@ -929,16 +1121,6 @@ } } - project.setParent( parentProject ); - - if ( parentProject != null ) - { - Artifact parentArtifact = artifactFactory.createParentArtifact( parentProject.getGroupId(), - parentProject.getArtifactId(), - parentProject.getVersion() ); - project.setParentArtifact( parentArtifact ); - } - // Must validate before artifact construction to make sure dependencies are good ModelValidationResult validationResult = validator.validate( model ); @@ -959,8 +1141,6 @@ project.setReportArtifacts( createReportArtifacts( projectId, project.getReportPlugins() ) ); project.setExtensionArtifacts( createExtensionArtifacts( projectId, project.getBuildExtensions() ) ); - - return project; } /** @@ -1026,6 +1206,12 @@ } MavenProject project = new MavenProject( model ); + + // If building from the middle of a lineage (i.e. child in a parent-child-grandchild project), child will be found in rawProjectCache, + // but without this, its parent may not be found in cache. Thus, location of pom.xml is important. And it can't be a directory as + // despite user documentation saying otherwise, there are a number of places in maven where File.getParent() is called assuming it + // will retrieve the directory where the pom.xml is stored. + project.setFile( new File( projectDir, "pom.xml" ) ); project.setActiveProfiles( activeProfiles ); @@ -1037,31 +1223,28 @@ { String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() ); - if ( StringUtils.isEmpty( parentModel.getGroupId() ) ) + if ( isParentIdExpanded( parentModel ) && isModelIdExpanded( model ) && + parentModel.getGroupId().equals( model.getGroupId() ) && + parentModel.getArtifactId().equals( model.getArtifactId() ) ) { - throw new ProjectBuildingException( projectId, "Missing groupId element from parent element" ); + throw new ProjectBuildingException( projectId, "Parent groupId/artifactId in '" + project.getFile() + + "' is a duplicate of the current project" ); } - else if ( StringUtils.isEmpty( parentModel.getArtifactId() ) ) - { - throw new ProjectBuildingException( projectId, "Missing artifactId element from parent element" ); - } - else if ( parentModel.getGroupId().equals( model.getGroupId() ) && - parentModel.getArtifactId().equals( model.getArtifactId() ) ) - { - throw new ProjectBuildingException( projectId, "Parent element is a duplicate of " + "the current project " ); - } - else if ( StringUtils.isEmpty( parentModel.getVersion() ) ) - { - throw new ProjectBuildingException( projectId, "Missing version element from parent element" ); - } // the only way this will have a value is if we find the parent on disk... File parentDescriptor = null; model = null; + MavenProject parentProject = null; + String parentKey = createCacheKey( parentModel.getGroupId(), parentModel.getArtifactId(), parentModel.getVersion() ); - MavenProject parentProject = (MavenProject) rawProjectCache.get( parentKey ); + + // If none of the parent needs to be interpolated, look up in the rawProjectCache + if ( isParentIdExpanded( parentModel ) ) + { + parentProject = (MavenProject) rawProjectCache.get( parentKey ); + } if ( parentProject != null ) { @@ -1121,21 +1304,20 @@ { Model candidateParent = readModel( projectId, parentDescriptor, strict ); - String candidateParentGroupId = candidateParent.getGroupId(); - if ( candidateParentGroupId == null && candidateParent.getParent() != null ) + if ( !isParentIdExpanded( parentModel ) || !isModelIdExpanded( candidateParent ) ) { - candidateParentGroupId = candidateParent.getParent().getGroupId(); + // Just use POM we found above us. We'll validate everything later + model = candidateParent; + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Using parent-POM from the project hierarcy for " + + project.getId() + " but id must still be validated." ); + } } - - String candidateParentVersion = candidateParent.getVersion(); - if ( candidateParentVersion == null && candidateParent.getParent() != null ) - { - candidateParentVersion = candidateParent.getParent().getVersion(); - } - - if ( parentModel.getGroupId().equals( candidateParentGroupId ) && + else if ( parentModel.getGroupId().equals( getModelGroupId( candidateParent ) ) && parentModel.getArtifactId().equals( candidateParent.getArtifactId() ) && - parentModel.getVersion().equals( candidateParentVersion ) ) + parentModel.getVersion().equals( getModelVersion( candidateParent ) ) ) { model = candidateParent; @@ -1160,6 +1342,25 @@ // only resolve the parent model from the repository system if we didn't find it on disk... if ( model == null ) { + if ( !isParentIdExpanded( parentModel ) ) + { + // Very hackish, but just in case var is defined in system properties... try to expand it + try + { + MavenProject tmpProj = expandProjectDefinition( project, externalProfileManager, project.getFile(), strict ); + Parent tmpParent = tmpProj.getModel().getParent(); + + if ( isParentIdExpanded( tmpParent ) ) + { + parentModel = tmpParent; + } + } + catch ( ModelInterpolationException e ) + { + getLogger().debug( "Unable to expand " + project.getId() + " when fetching parent from repository." ); + } + } + // MNG-2302: parent's File was being populated incorrectly when parent is loaded from repo. // keep this in line with other POMs loaded from the repository...the file should be null. parentDescriptor = null; @@ -1205,8 +1406,8 @@ projectId + " has wrong packaging: " + model.getPackaging() + ". Must be 'pom'." ); } - File parentProjectDir = null; - if ( parentDescriptor != null ) + File parentProjectDir = parentDescriptor; + if ( parentDescriptor != null && !parentDescriptor.isDirectory() ) // We test elsewhere that descriptor is a dir, test here too { parentProjectDir = parentDescriptor.getParentFile(); } @@ -1371,6 +1572,227 @@ } } + /** + * If the parent version is empty, find it in the parent-pom(s) and repair it in our project. + */ + private String fixParentVersion( MavenProject project ) throws ProjectBuildingException + { + Parent projectModelParent = project.getModel().getParent(); + String version = projectModelParent.getVersion(); + + // If our project has no version, walk up our parents until we find one + if ( StringUtils.isEmpty( version ) ) + { + version = findParentVersion( project ); + + if ( StringUtils.isEmpty( version ) ) + { + throw new ProjectBuildingException( project.getId(), "Unable to find version in parent pom-s" ); + } + + if ( StringUtils.isEmpty( project.getVersion() ) ) + { + // If our version was being implied from the parent version, then set our version + project.setVersion( version ); + } + + projectModelParent.setVersion( version ); + } + + return version; + } + + /** + * If the parent groupId is empty, find it in the parent-pom(s) and repair it in our project. + */ + private String fixParentGroupId( MavenProject project ) throws ProjectBuildingException + { + Parent parent = project.getModel().getParent(); + String groupId = parent.getGroupId(); + + if ( StringUtils.isEmpty( groupId ) ) + { + groupId = findParentGroupId( project ); + + if ( StringUtils.isEmpty( groupId ) ) + { + throw new ProjectBuildingException( project.getId(), "Unable to find groupId in parent pom-s" ); + } + + if ( StringUtils.isEmpty( project.getGroupId() ) ) + { + // If our groupId was being implied from the parent groupId, then set our groupId + project.setGroupId( groupId ); + } + + parent.setGroupId( groupId ); + } + + return groupId; + } + + /** + * If the parent artifactId is empty, find it in the parent-pom(s) and repair it in our project. + */ + private String fixParentArtifactId( MavenProject project ) throws ProjectBuildingException + { + Parent parent = project.getModel().getParent(); + String artifactId = parent.getArtifactId(); + + if ( StringUtils.isEmpty( artifactId ) ) + { + artifactId = findParentArtifactId( project ); + + if ( StringUtils.isEmpty( artifactId ) ) + { + throw new ProjectBuildingException( project.getId(), "Unable to find artifactId in parent pom-s" ); + } + + parent.setArtifactId( artifactId ); + } + + return artifactId; + } + + /** + * Recurse up our lineage (parents) until we find a parent whose version is specified. + */ + private String findParentVersion( MavenProject project ) + { + getLogger().debug( project.getId() + ": looking for parent version" ); + MavenProject candidateParentProject = project.getParent(); + + if ( candidateParentProject == null ) + { + getLogger().debug( project.getId() + ": found no parent!"); + return null; // no more parents to chase down! User probably didn't put a version anywhere! + } + + String version = candidateParentProject.getVersion(); + + if ( StringUtils.isEmpty( version ) ) + { + return findParentVersion( candidateParentProject ); + } + + getLogger().debug( "" + project.getId() + ": returning ver from " + candidateParentProject.getId() ); + + return version; + } + + /** + * Recurse up our lineage (parents) until we find a parent whose groupId is specified. + */ + private String findParentGroupId( MavenProject project ) + { + MavenProject candidateParentProject = project.getParent(); + + if ( candidateParentProject == null ) + { + return null; // no more parents to chase down + } + + String groupId = candidateParentProject.getGroupId(); + + if ( StringUtils.isEmpty( groupId ) ) + { + return findParentGroupId( candidateParentProject ); + } + + return groupId; + } + + /** + * Recurse up our lineage (parents) until we find a parent whose artifactId is specified. + */ + private String findParentArtifactId( MavenProject project ) + { + MavenProject candidateParentProject = project.getParent(); + + if ( candidateParentProject == null ) + { + return null; // no more parents to chase down + } + + String artifactId = candidateParentProject.getArtifactId(); + + if ( StringUtils.isEmpty( artifactId ) ) + { + return findParentArtifactId( candidateParentProject ); + } + + return artifactId; + } + + /** + * Return true if there are no more variables to be expanded in a model's parent tags. + * + * Note that this will also return false if there is a variable that simply cannot be expanded because it is not defined + * or was misspelled, etc. + * + * @param parent a model's parent tag + * @return true if there are no more variables to be expanded in a model's parent tags. + */ + private static boolean isParentIdExpanded( Parent parent ) + { + return ( !StringUtils.isEmpty( parent.getVersion() ) && + !StringUtils.isEmpty( parent.getGroupId() ) && + !StringUtils.isEmpty( parent.getArtifactId() ) && + parent.getVersion().indexOf( "${" ) == -1 && + parent.getGroupId().indexOf( "${" ) == -1 && + parent.getArtifactId().indexOf( "${" ) == -1 ); + } + + /** + * Return true if there are no more variables to be expanded the tags that comprise an artifact definition. + * + * Note that this will also return false if there is a variable that simply cannot be expanded because it is not defined + * or was misspelled, etc. + * + * @param model the model to inspect + * @return true if there are no more variables to be expanded in the definition of a model's artifact. + */ + private static boolean isModelIdExpanded( Model model ) + { + String version = getModelVersion( model ); + String groupId = getModelGroupId( model ); + String artifactId = model.getArtifactId(); // should never be empty/missing + + return ( !StringUtils.isEmpty( version ) && + !StringUtils.isEmpty( groupId ) && + version.indexOf( "${" ) == -1 && + groupId.indexOf( "${" ) == -1 && + artifactId.indexOf( "${" ) == -1 ); + } + + /** + * Sometimes a model's GroupId is specified only in its parent. Fetch whatever we can find. + */ + private static String getModelGroupId( Model model ) + { + if ( model != null && model.getGroupId() != null ) + return model.getGroupId(); + + if ( model == null || model.getParent() == null ) + return null; + + return model.getParent().getGroupId(); + } + + /** + * Sometimes a model's Version is specified only in its parent. Fetch whatever we can find. + */ + private static String getModelVersion( Model model ) + { + if ( model != null && model.getVersion() != null ) + return model.getVersion(); + + if ( model == null || model.getParent() == null ) + return null; + + return model.getParent().getVersion(); + } + private static String createCacheKey( String groupId, String artifactId, String version ) { return groupId + ":" + artifactId + ":" + version; Index: maven-project/src/main/java/org/apache/maven/project/artifact/ActiveProjectArtifact.java =================================================================== --- maven-project/src/main/java/org/apache/maven/project/artifact/ActiveProjectArtifact.java (revision 510454) +++ maven-project/src/main/java/org/apache/maven/project/artifact/ActiveProjectArtifact.java (working copy) @@ -49,6 +49,8 @@ private final MavenProject project; + private String modelRepresentation; + public ActiveProjectArtifact( MavenProject project, Artifact artifact ) { this.artifact = artifact; @@ -296,4 +298,14 @@ { artifact.setOptional( optional ); } + + public String getModelRepresentation() + { + return modelRepresentation; + } + + public void setModelRepresentation( String modelRepresentation ) + { + this.modelRepresentation = modelRepresentation; + } } Index: maven-project/src/main/java/org/apache/maven/project/artifact/ProjectArtifactMetadata.java =================================================================== --- maven-project/src/main/java/org/apache/maven/project/artifact/ProjectArtifactMetadata.java (revision 510454) +++ maven-project/src/main/java/org/apache/maven/project/artifact/ProjectArtifactMetadata.java (working copy) @@ -80,20 +80,34 @@ File destination = new File( localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata( this, remoteRepository ) ); - // ---------------------------------------------------------------------------- - // I'm fully aware that the file could just be moved using File.rename but - // there are bugs in various JVM that have problems doing this across - // different filesystem. So we'll incur the small hit to actually copy - // here and be safe. jvz. - // ---------------------------------------------------------------------------- - - try + if ( artifact.getModelRepresentation() != null ) { - FileUtils.copyFile( file, destination ); + try + { + FileUtils.fileWrite( destination.getAbsolutePath(), artifact.getModelRepresentation() ); + } + catch ( IOException e ) + { + throw new RepositoryMetadataStoreException( "Error writing POM to the local repository.", e ); + } } - catch ( IOException e ) + else { - throw new RepositoryMetadataStoreException( "Error copying POM to the local repository.", e ); + // ---------------------------------------------------------------------------- + // I'm fully aware that the file could just be moved using File.rename but + // there are bugs in various JVM that have problems doing this across + // different filesystem. So we'll incur the small hit to actually copy + // here and be safe. jvz. + // ---------------------------------------------------------------------------- + + try + { + FileUtils.copyFile( file, destination ); + } + catch ( IOException e ) + { + throw new RepositoryMetadataStoreException( "Error copying POM to the local repository.", e ); + } } }