MNG-5444 fixed building MavenProject from ModelSource

To maintain compatibility with (the extremely unlikely) existing
implementations of ModelSource, the new API methods are provided
via optional ModelSource2 interface. Updated javadoc to encourage
use of the new API.

Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
This commit is contained in:
Igor Fedorenko 2013-03-01 09:19:22 -05:00
parent ef06aab96d
commit 9b58a3cfdc
11 changed files with 170 additions and 34 deletions

View File

@ -24,6 +24,7 @@ import java.util.List;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.building.ModelSource; import org.apache.maven.model.building.ModelSource;
import org.apache.maven.model.building.ModelSource2;
/** /**
* Builds in-memory descriptions of projects. * Builds in-memory descriptions of projects.
@ -74,6 +75,8 @@ public interface ProjectBuilder
* @param request The project building request that holds further parameters, must not be {@code null}. * @param request The project building request that holds further parameters, must not be {@code null}.
* @return The result of the project building, never {@code null}. * @return The result of the project building, never {@code null}.
* @throws ProjectBuildingException If the project descriptor could not be successfully built. * @throws ProjectBuildingException If the project descriptor could not be successfully built.
*
* @see ModelSource2
*/ */
ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request ) ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request )
throws ProjectBuildingException; throws ProjectBuildingException;

View File

@ -24,6 +24,8 @@ import java.util.Properties;
import org.apache.maven.AbstractCoreMavenComponentTestCase; import org.apache.maven.AbstractCoreMavenComponentTestCase;
import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.building.FileModelSource;
import org.apache.maven.model.building.ModelSource;
public class ProjectBuilderTest public class ProjectBuilderTest
extends AbstractCoreMavenComponentTestCase extends AbstractCoreMavenComponentTestCase
@ -49,4 +51,18 @@ public class ProjectBuilderTest
project.getCompileClasspathElements(); project.getCompileClasspathElements();
} }
public void testBuildFromModelSource()
throws Exception
{
File pomFile = new File( "src/test/resources/projects/modelsource/module01/pom.xml" );
MavenSession mavenSession = createMavenSession( pomFile );
ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest();
configuration.setRepositorySession( mavenSession.getRepositorySession() );
ModelSource modelSource = new FileModelSource( pomFile );
ProjectBuildingResult result =
lookup( org.apache.maven.project.ProjectBuilder.class ).build( modelSource, configuration );
assertNotNull( result.getProject().getParentFile() );
}
} }

View File

@ -0,0 +1,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>test.readparent</groupId>
<artifactId>local-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>module01</artifactId>
</project>

View File

@ -0,0 +1,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test.readparent</groupId>
<artifactId>local-parent</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<modules>
<module>module</module>
</modules>
</project>

View File

@ -257,8 +257,8 @@ public class DefaultModelBuilder
problems.setRootModel( inputModel ); problems.setRootModel( inputModel );
ModelData resultData = new ModelData( inputModel ); ModelData resultData = new ModelData( request.getModelSource(), inputModel );
ModelData superData = new ModelData( getSuperModel() ); ModelData superData = new ModelData( null, getSuperModel() );
Collection<String> parentIds = new LinkedHashSet<String>(); Collection<String> parentIds = new LinkedHashSet<String>();
parentIds.add( ModelProblemUtils.toId( inputModel ) ); parentIds.add( ModelProblemUtils.toId( inputModel ) );
@ -304,7 +304,7 @@ public class DefaultModelBuilder
configureResolver( request.getModelResolver(), tmpModel, problems ); configureResolver( request.getModelResolver(), tmpModel, problems );
currentData = readParent( tmpModel, request, problems ); currentData = readParent( tmpModel, currentData.getSource(), request, problems );
if ( currentData == null ) if ( currentData == null )
{ {
@ -644,7 +644,7 @@ public class DefaultModelBuilder
return result; return result;
} }
private ModelData readParent( Model childModel, ModelBuildingRequest request, private ModelData readParent( Model childModel, ModelSource childSource, ModelBuildingRequest request,
DefaultModelProblemCollector problems ) DefaultModelProblemCollector problems )
throws ModelBuildingException throws ModelBuildingException
{ {
@ -662,7 +662,7 @@ public class DefaultModelBuilder
if ( parentData == null ) if ( parentData == null )
{ {
parentData = readParentLocally( childModel, request, problems ); parentData = readParentLocally( childModel, childSource, request, problems );
if ( parentData == null ) if ( parentData == null )
{ {
@ -683,9 +683,10 @@ public class DefaultModelBuilder
File pomFile = parentData.getModel().getPomFile(); File pomFile = parentData.getModel().getPomFile();
if ( pomFile != null ) if ( pomFile != null )
{ {
File expectedParentFile = getParentPomFile( childModel ); ModelSource expectedParentSource = getParentPomFile( childModel, childSource );
if ( !pomFile.equals( expectedParentFile ) ) if ( expectedParentSource instanceof ModelSource2
&& !pomFile.toURI().equals( ( (ModelSource2) expectedParentSource ).getLocationURI() ) )
{ {
parentData = readParentExternally( childModel, request, problems ); parentData = readParentExternally( childModel, request, problems );
} }
@ -710,18 +711,24 @@ public class DefaultModelBuilder
return parentData; return parentData;
} }
private ModelData readParentLocally( Model childModel, ModelBuildingRequest request, private ModelData readParentLocally( Model childModel, ModelSource childSource, ModelBuildingRequest request,
DefaultModelProblemCollector problems ) DefaultModelProblemCollector problems )
throws ModelBuildingException throws ModelBuildingException
{ {
File pomFile = getParentPomFile( childModel ); ModelSource candidateSource = getParentPomFile( childModel, childSource );
if ( pomFile == null || !pomFile.isFile() ) if ( candidateSource == null )
{ {
return null; return null;
} }
Model candidateModel = readModel( null, pomFile, request, problems ); File pomFile = null;
if ( candidateSource instanceof FileModelSource )
{
pomFile = ( (FileModelSource) candidateSource ).getPomFile();
}
Model candidateModel = readModel( candidateSource, pomFile, request, problems );
String groupId = candidateModel.getGroupId(); String groupId = candidateModel.getGroupId();
if ( groupId == null && candidateModel.getParent() != null ) if ( groupId == null && candidateModel.getParent() != null )
@ -761,16 +768,14 @@ public class DefaultModelBuilder
return null; return null;
} }
ModelData parentData = new ModelData( candidateModel, groupId, artifactId, version ); ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version );
return parentData; return parentData;
} }
private File getParentPomFile( Model childModel ) private ModelSource getParentPomFile( Model childModel, ModelSource source )
{ {
File projectDirectory = childModel.getProjectDirectory(); if ( !( source instanceof ModelSource2 ) )
if ( projectDirectory == null )
{ {
return null; return null;
} }
@ -782,16 +787,7 @@ public class DefaultModelBuilder
return null; return null;
} }
parentPath = parentPath.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar ); return ( (ModelSource2) source ).getRelatedSource( parentPath );
File pomFile = new File( new File( projectDirectory, parentPath ).toURI().normalize() );
if ( pomFile.isDirectory() )
{
pomFile = modelProcessor.locatePom( pomFile );
}
return pomFile;
} }
private ModelData readParentExternally( Model childModel, ModelBuildingRequest request, private ModelData readParentExternally( Model childModel, ModelBuildingRequest request,
@ -867,7 +863,7 @@ public class DefaultModelBuilder
Model parentModel = readModel( modelSource, null, lenientRequest, problems ); Model parentModel = readModel( modelSource, null, lenientRequest, problems );
ModelData parentData = new ModelData( parentModel, groupId, artifactId, version ); ModelData parentData = new ModelData( modelSource, parentModel, groupId, artifactId, version );
return parentData; return parentData;
} }

View File

@ -109,8 +109,12 @@ public class DefaultModelBuildingRequest
return this; return this;
} }
public ModelSource getModelSource() public synchronized ModelSource getModelSource()
{ {
if ( modelSource == null && pomFile != null )
{
modelSource = new FileModelSource( pomFile );
}
return modelSource; return modelSource;
} }

View File

@ -23,6 +23,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI;
/** /**
* Wraps an ordinary {@link File} as a model source. * Wraps an ordinary {@link File} as a model source.
@ -30,10 +31,9 @@ import java.io.InputStream;
* @author Benjamin Bentmann * @author Benjamin Bentmann
*/ */
public class FileModelSource public class FileModelSource
implements ModelSource implements ModelSource2
{ {
private final File pomFile;
private File pomFile;
/** /**
* Creates a new model source backed by the specified file. * Creates a new model source backed by the specified file.
@ -76,4 +76,28 @@ public class FileModelSource
return getLocation(); return getLocation();
} }
public ModelSource2 getRelatedSource( String relPath )
{
relPath = relPath.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar );
File relatedPom = new File( pomFile.getParentFile(), relPath );
if ( relatedPom.isDirectory() )
{
// TODO figure out how to reuse ModelLocator.locatePom(File) here
relatedPom = new File( relatedPom, "pom.xml" );
}
if ( relatedPom.isFile() && relatedPom.canRead() )
{
return new FileModelSource( new File( relatedPom.toURI().normalize() ) );
}
return null;
}
public URI getLocationURI()
{
return pomFile.toURI();
}
} }

View File

@ -83,7 +83,7 @@ interface ModelCacheTag<T>
public ModelData intoCache( ModelData data ) public ModelData intoCache( ModelData data )
{ {
Model model = ( data.getModel() != null ) ? data.getModel().clone() : null; Model model = ( data.getModel() != null ) ? data.getModel().clone() : null;
return new ModelData( model, data.getGroupId(), data.getArtifactId(), data.getVersion() ); return new ModelData( data.getSource(), model, data.getGroupId(), data.getArtifactId(), data.getVersion() );
} }
public ModelData fromCache( ModelData data ) public ModelData fromCache( ModelData data )

View File

@ -32,6 +32,7 @@ import org.apache.maven.model.Profile;
*/ */
class ModelData class ModelData
{ {
private final ModelSource source;
private Model model; private Model model;
@ -50,8 +51,9 @@ class ModelData
* *
* @param model The model to wrap, may be {@code null}. * @param model The model to wrap, may be {@code null}.
*/ */
public ModelData( Model model ) public ModelData( ModelSource source, Model model )
{ {
this.source = source;
this.model = model; this.model = model;
} }
@ -63,14 +65,20 @@ class ModelData
* @param artifactId The effective artifact identifier of the model, may be {@code null}. * @param artifactId The effective artifact identifier of the model, may be {@code null}.
* @param version The effective version of the model, may be {@code null}. * @param version The effective version of the model, may be {@code null}.
*/ */
public ModelData( Model model, String groupId, String artifactId, String version ) public ModelData( ModelSource source, Model model, String groupId, String artifactId, String version )
{ {
this.source = source;
this.model = model; this.model = model;
setGroupId( groupId ); setGroupId( groupId );
setArtifactId( artifactId ); setArtifactId( artifactId );
setVersion( version ); setVersion( version );
} }
public ModelSource getSource()
{
return source;
}
/** /**
* Gets the model being wrapped. * Gets the model being wrapped.
* *

View File

@ -24,8 +24,12 @@ import java.io.InputStream;
/** /**
* Provides access to the contents of a POM independently of the backing store (e.g. file system, database, memory). * Provides access to the contents of a POM independently of the backing store (e.g. file system, database, memory).
* <p>
* This interface does not support loading of parent POM(s) from the same backing store, integrators are strongly
* encouraged to implement {@link ModelSource2} instead of implementing this interface directly.
* *
* @author Benjamin Bentmann * @author Benjamin Bentmann
* @see ModelSource2
*/ */
public interface ModelSource public interface ModelSource
{ {

View File

@ -0,0 +1,56 @@
package org.apache.maven.model.building;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.net.URI;
/**
* Provides access to the contents of a POM independently of the backing store (e.g. file system, database, memory).
* <p>
* Unlike {@link ModelSource}, this interface supports loading of parent POM(s) from the same backing store and allows
* construction of MavenProject instances without the need to have parent POM(s) available from local or remote
* repositories.
* <p>
* ModelSource2 instances are cached in {@link ModelBuildingRequest#getModelCache()}. Implementations must guarantee
* that the connection to the backing store remains active until request's {@link ModelCache} is discarded or flushed.
*/
public interface ModelSource2
extends ModelSource
{
/**
* Returns model source identified by a path relative to this model source POM. Implementation <strong>MUST</strong>
* be able to accept <code>relPath</code> parameter values that
* <ul>
* <li>use either / or \ file path separator</li>
* <li>have .. parent directory references</li>
* <li>point either at file or directory, in the latter case POM file name 'pom.xml' needs to be used by the
* requested model source.</li>
* </ul>
*
* @param relPath is the path of the requested model source relative to this model source POM.
* @return related model source or <code>null</code> if no such model source.
*/
ModelSource2 getRelatedSource( String relPath );
/**
* Returns location of the POM, never <code>null</code>.
*/
URI getLocationURI();
}