19

如何获得完全解析的 pom 文件模型?

这基本上是对我如何以编程方式构建 pom 文件的有效模型的改写?

我正在构建一个针对一组模块执行一些验证规则的 Maven 插件。这些模块的 pom 文件可用,但在执行插件时它们不是反应器的一部分。

我可以使用这种方法读取一个 pom 文件并获取相应的 Model 对象(为简单起见,删除了异常处理):

private Model pomToModel(String pathToPom) throws Exception {
    BufferedReader in = new BufferedReader(new FileReader(pathToPom));
    MavenXpp3Reader reader = new MavenXpp3Reader();
    Model model = reader.read(in);
    return model;
}

它可以工作,但模型对象只具有与 pom 文件相同的信息。

如何改进该方法以便获得“完全解析”的模型对象?完全解决,我的意思是:所有的传递依赖和来自父 pom 的所有其他内容。

干杯!

4

6 回答 6

12

我做到了 :-)

help:effective-pom 和 dependency:tree 真的一点帮助都没有。

我必须看看 maven 如何为注入 mojo 的 MavenProject 构建模型。help:effective-pom 已经接收到解析的模型,而 dependency:tree 只构建了一个 DependencyGraph,但它不会将 pom 的整个模型加载到内存中。

通过使用下面的代码,我能够从父级获取包含所有信息的模型对象,解析 ${property} 表达式,并扩展传递依赖项。

就是这样:

1) 获取模型解析器

您将需要一个接口 org.apache.maven.model.resolution.ModelResolver的实例。不幸的是,maven 不能通过依赖注入轻松提供一个(至少我找不到),所以我们必须构建一个。为了让事情变得更好,该接口的唯一两个实现是包保护的,因此您需要使用一些反射魔法来实例化它。实现它的具体类是DefaultModelResolverProjectModelResolver。我能够像这样构建一个 DefaultModelResolver

/**
 * The Maven Project Object
 * 
 * @parameter expression="${project}"
 * @required2.0
 * @readonly
 */
protected MavenProject project;

/**
 * @component
 */
protected ArtifactResolver artifactResolver;

/**
 * @component
 */
protected RemoteRepositoryManager remoteRepositoryManager;

private Object invoke( Object object, String method )
        throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    return object.getClass().getMethod( method ).invoke( object );
}

private org.apache.maven.model.resolution.ModelResolver makeModelResolver() throws MojoExecutionException {
    try {
        ProjectBuildingRequest projectBuildingRequest =
        (ProjectBuildingRequest) invoke( project, "getProjectBuildingRequest" );

        Class c = Class.forName("org.apache.maven.repository.internal.DefaultModelResolver");
        Constructor ct = c.getConstructor(new Class[]{RepositorySystemSession.class, 
                RequestTrace.class, String.class,
                ArtifactResolver.class, RemoteRepositoryManager.class,
                List.class});
        ct.setAccessible(true);
        return (org.apache.maven.model.resolution.ModelResolver) ct.newInstance(new Object[]{
                projectBuildingRequest.getRepositorySession(), 
                null, null, artifactResolver, remoteRepositoryManager, 
                project.getRemoteProjectRepositories()});
    } catch (Exception e) {
        throw new MojoExecutionException("Error instantiating DefaultModelResolver", e);
    }
}

2) 建立模型

当你有一个 modelResolver 时,你可以像这样从 pom 文件构建模型:

public Model resolveEffectiveModel(File pomfile) {
    try {
        return modelBuilder.build(makeModelBuildRequest(pomfile)).getEffectiveModel();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }   
}

private ModelBuildingRequest makeModelBuildRequest(File artifactFile) {
    DefaultModelBuildingRequest mbr = new DefaultModelBuildingRequest();
    mbr.setPomFile(artifactFile);
    mbr.setModelResolver(modelResolver); // <-- the hard-to-get modelResolver
    return mbr;
}

看起来不漂亮,但它对我有用..:P

于 2012-10-04T20:59:30.840 回答
7

Romain 在上面提供了很好的答案,但它使用的是从 maven 3.x 中删除的弃用类。更新版本是这样的:

@Parameter( defaultValue = "${project}", readonly = true )
private MavenProject project;

@Component
private RepositorySystem repositorySystem;

@Component
private ProjectBuilder mavenProjectBuilder;

@Parameter( defaultValue = "${session}", readonly = true )
private MavenSession session;

private MavenProject getMavenProject(String groupId, String artifactId, String version) throws ProjectBuildingException {

    Artifact pomArtifact = repositorySystem.createProjectArtifact(groupId, artifactId, version);
    ProjectBuildingResult build = mavenProjectBuilder.build(pomArtifact, session.getProjectBuildingRequest());

    return build.getProject();

}

一个工作示例在hierarchy-maven-plugin

于 2016-09-21T06:51:54.467 回答
4

也许对你来说为时已晚,但如果它可以在未来帮助别人。所以我这样做了:

@Component
private RepositorySystem repositorySystem;

@Component
private MavenProjectBuilder mavenProjectBuilder;

@Parameter(property = "project.remoteArtifactRepositories")
protected List<ArtifactRepository> remoteRepositories;

@Parameter(property = "localRepository")
protected ArtifactRepository localRepository;

...
Artifact pomArtifact = repositorySystem.createProjectArtifact(groupId, artifactId,version);
MavenProject project = mavenProjectBuilder.buildFromRepository(pomArtifact
                          , remoteRepositories, localRepository);

就是这样。它应该工作。如果您有一些特殊的包装(例如捆绑...) 在目标 pom 项目中,请确保与这些包装关联的插件安装在您当前的项目中。

于 2014-10-29T03:26:04.940 回答
2

您寻找的源代码在help:effective-pom某个地方。

--- 编辑更新 ---

快速浏览后,您似乎需要Project从读取的 pom.xml 构建一个 Maven。这可能会涉及许多步骤,包括解析 POM 的父项目、下载和解析其他 Maven 插件工件以及将所有引用连接在一起。

单独阅读儿童级别的 pom 是行不通的。

于 2012-07-17T15:03:16.430 回答
1

执行“mvn help:effective-pom”时,maven 帮助插件会做类似的事情。

http://svn.apache.org/viewvc/maven/plugins/tags/maven-help-plugin-2.1.1/src/main/java/org/apache/maven/plugins/help/EffectivePomMojo.java?view =来源的标记

我认为这不会显示传递依赖。

mvn dependency:tree 目标可以做到这一点:http: //svn.apache.org/viewvc/maven/plugins/tags/maven-dependency-plugin-2.4/src/main/java/org/apache/maven/plugin/dependency /TreeMojo.java?view=标记

也许你可以创造两者的混合物?

于 2012-07-17T15:07:19.040 回答
1

以防万一有人需要,这里有一个在 Groovy 中运行的示例。它使用 Grape 动态加载使用 pom.xml 所需的依赖项。它同时加载运行时类路径和测试类路径。

@Grapes([
         @Grab(group='org.apache.maven', module='maven-core', version='3.0.5'),
         @Grab(group='org.apache.maven', module='maven-compat', version='3.0.5'),
         @Grab(group='com.jcabi', module='jcabi-aether', version='0.10.1')
         ])

         // http://www.programcreek.com/java-api-examples/index.php?api=org.apache.maven.project.MavenProjectBuilder See # 20


import org.codehaus.plexus.DefaultPlexusContainer
import org.apache.maven.project.MavenProjectBuilder
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.project.DefaultProjectBuilderConfiguration
import org.apache.maven.artifact.repository.DefaultArtifactRepository
import com.jcabi.aether.Aether
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.artifact.Artifact;


container=new DefaultPlexusContainer();
projectBuilder=(MavenProjectBuilder)container.lookup(MavenProjectBuilder.class.getName());
layout=(ArtifactRepositoryLayout)container.lookup(ArtifactRepositoryLayout.class.getName(),"default");

def projectInfo(localRepoUrl, pom){

    File pomFile = new File(pom);
    String localRepoUrl2 = "file://" + localRepoUrl;
    File local = new File(localRepoUrl);



    ArtifactRepository localRepo=new DefaultArtifactRepository("local",localRepoUrl2,layout);
    pbConfig=new DefaultProjectBuilderConfiguration().setLocalRepository(localRepo);
    project = projectBuilder.build( pomFile, pbConfig );
    aether = new Aether(project, local);
    [ runtime: resolveDependencies(aether, project, "runtime"),
      test : resolveDependencies(aether, project, "test") ];
}


def resolveDependencies (aether, project, scope) {
    depLists = project.getDependencies().collect { 

        art = new DefaultArtifact(it.getGroupId(), it.getArtifactId(), it.getClassifier(), it.getType(), 
                                  it.getVersion());
        Collection<Artifact> deps = aether.resolve( art, scope );

        deps.collect{  it.getFile().getAbsolutePath() }

    }

    [ dependencies : depLists.collect {it.first()},  classpath : depLists.flatten() ]
}



println projectInfo("c:/Users/lpmsmith/.m2/repository", "pom.xml");
于 2014-11-24T21:18:42.650 回答