How do I obtain a fully resolved Model of a pom file?

15,162

Solution 1

I did it :-)

help:effective-pom and dependency:tree really did not help at all.

I had to look at how maven builds the Model for the MavenProject that gets injected in the mojo. help:effective-pom already receives the resolved Model, and dependency:tree only builds a DependencyGraph, but it doesn't load the whole model for a pom into memory.

By using the code below I was able to get a Model object with all the information from the parent, with resolved ${property} expressions, and expanded transitive dependencies.

Here's how:

1) Get a ModelResolver

You will need an instance of interface org.apache.maven.model.resolution.ModelResolver. Unfortunately, maven doesn't provide one easily via dependency injection (at least I couldn't find one), so we'll have to build one. To make things even better, the only two implementations of that interface are package protected, so you need to use some reflection magic to instantiate it. The concrete classes that implement it are DefaultModelResolver and ProjectModelResolver. I was able to build a DefaultModelResolver like this

/**
 * 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) Build the Model

When you have a modelResolver, you can build the Model from a pom file like this:

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;
}

Doesn't look pretty, but it worked for me.. :P

Solution 2

Romain provided the good answer above, but it was using a deprecated class that was removed from maven 3.x The updated version is this :

@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();

}

A working example is in the hierarchy-maven-plugin

Solution 3

Maybe is too late for you but if it can help others in the future. So I did it like that:

@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);

And that's it. It should work. If you have some special packaging (e.g. bundle...) In the target pom project make sure the plugins associated to those packaging are installed in your current project.

Solution 4

The source code you seek is in help:effective-pom, somewhere.

--- Edit update ---

After a quick glance, it would seem that you would need to build a Maven Project from the read pom. This likely will involve a number of steps that include resolution of the parent project of the POM, downloading and parsing other Maven plugin artifacts and wiring all of the references together.

Reading the child-level pom alone won't do it.

Solution 5

the maven help plugin does something similar when "mvn help:effective-pom" is executed.

see 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=markup for the sources.

I think this will not show the transitive depedencies.

The mvn dependency:tree goal does that: http://svn.apache.org/viewvc/maven/plugins/tags/maven-dependency-plugin-2.4/src/main/java/org/apache/maven/plugin/dependency/TreeMojo.java?view=markup

maybe you can create a mixture of both?

Share:
15,162
Tony Lâmpada
Author by

Tony Lâmpada

Building software since 2000. Creator of freedomsponsors.org

Updated on June 30, 2022

Comments

  • Tony Lâmpada
    Tony Lâmpada almost 2 years

    How do I obtain a fully resolved Model of a pom file?

    This is basically a rephrasing of How can i programmaticaly build the effective model of a pom file?

    I'm building a maven plugin that performs some validation rules against a set of modules. Those modules' pom files are available but they're not part of the reactor when the plugin is executed.

    I can read a pom file and obtain the corresponding Model object using this method (removed exception handling for simplicity):

    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;
    }
    

    And it works but the Model object has only the same information that the pom file has.

    How can I improve that method so that I can obtain a "fully resolved" Model object? By fully resolved, I mean: with all the transitive dependencies and everything else from the parent poms.

    Cheers!

  • Tony Lâmpada
    Tony Lâmpada almost 12 years
    Thanks! I'll take a look. BTW, you should check out that apache server of yours ;-)
  • Tony Lâmpada
    Tony Lâmpada almost 12 years
    Thanks, I'll take a look on that too!
  • Edwin Buck
    Edwin Buck almost 12 years
    @TonyLâmpada Good luck, and that Apache server does serve a purpose, if you know the right URLs. :) It's just not a vanity web site, if that was what you were expecting.
  • Tony Lâmpada
    Tony Lâmpada over 11 years
    help:effective-pom doesn't help me, its the other way around - it uses the already built model that it gets from project.getModel(). But that model was already pre-built somewhere else.
  • Max Spring
    Max Spring over 10 years
    It works for me, too. But I really hope there's a cleaner way.
  • Edwin Buck
    Edwin Buck over 9 years
    Since effective-pom does that for the current maven Project, typically it doesn't help as much as you might think. effective-pom assumes that the normally existing maven framework has already done some of the work. What is called for is more of a second maven project, and just enough to bootstrap that into a fully resolved pom.
  • Kalpesh Soni
    Kalpesh Soni over 9 years
    do you know if maven-dependency-plugin does something similar as your code?
  • eerriicc
    eerriicc almost 9 years
    Thanks, you saved my day! :-)
  • Peter Szanto
    Peter Szanto over 7 years
    @romain-gilles what version of dependencies do you use? It seems like MavenProjectBuilder is deprecated in 3.x, but RepositorySystem is not available in 2.2.1. In 3.x ProjectBuilder is available, but it can't build MavenProject
  • Peter Szanto
    Peter Szanto over 7 years
    I found the answer to my question, details below stackoverflow.com/a/39609354/157591
  • Puce
    Puce over 7 years
    ProjectModelResolver is not package-private but it requires a package-private class ReactorModelPool
  • DarVar
    DarVar almost 7 years
    How can I inject RepositorySystem ProjectBuilder and MavenSession into my own Component
  • DarVar
    DarVar almost 7 years
    @Component(role = MyResolver.class, instantiationStrategy = "per-lookup") public class MyResolver {....}