2012-07-17 6 views
12

¿Cómo obtengo un Modelo completamente resuelto de un archivo pom?¿Cómo obtengo un Modelo completamente resuelto de un archivo pom?

Esta es básicamente una reformulación de How can i programmaticaly build the effective model of a pom file?

Estoy construyendo un plugin experto que realiza algunas reglas de validación contra un conjunto de módulos. Los archivos pom de esos módulos están disponibles pero no forman parte del reactor cuando se ejecuta el complemento.

puedo leer un archivo POM y obtener el objeto de modelo correspondiente usando este método (manejo de excepciones eliminado por simplicidad):

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

y funciona pero el objeto de modelo sólo tiene la misma información que el archivo POM tiene.

¿Cómo puedo mejorar ese método para poder obtener un objeto de modelo "totalmente resuelto"? Completamente resuelto, quiero decir: con todas las dependencias transitivas y todo lo demás de las poms principales.

¡Salud!

+0

¿Puedes elaborar un poco sobre las reglas de validación? – khmarbaise

Respuesta

9

lo hice :-)

ayuda: efectivo-pom y dependencia: Árbol realmente hizo no ayuda en absoluto.

Tuve que ver cómo maven construye el Modelo para el MavenProject que se inyecta en el mojo. help: effective-pom ya recibe el Modelo resuelto, y la dependencia: tree solo crea un DependencyGraph, pero no carga todo el modelo para un pom en la memoria.

Al usar el código siguiente, pude obtener un objeto Modelo con toda la información del padre, con expresiones $ {property} resueltas y dependencias transitivas ampliadas.

Así es como:

1) Obtener una ModelResolver

Usted necesitará una instancia de interfaz de org.apache.maven.model.resolution.ModelResolver. Desafortunadamente, maven no proporciona una inyección de dependencia fácilmente (al menos no pude encontrar una), así que tendremos que crear una. Para hacer las cosas aún mejor, las únicas dos implementaciones de esa interfaz están protegidas por paquete, por lo que debe usar alguna magia de reflexión para crear una instancia. Las clases concretas que lo implementan son DefaultModelResolver y ProjectModelResolver. que fue capaz de construir una DefaultModelResolver como esto

/** 
* 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) construir el modelo

Cuando se tiene un modelResolver, se puede construir el modelo de un archivo pom así:

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

No se ve bonito, pero funcionó para mí ..: P

+0

Funciona para mí, también. Pero realmente espero que haya una manera más limpia. –

+0

¿sabes si maven-dependency-plugin hace algo similar a tu código? –

+0

ProjectModelResolver no es un paquete privado, pero requiere una clase privada de paquete ReactorModelPool – Puce

1

El código fuente que busca está en help:effective-pom, en algún lado.

--- --- Editar actualización

Después a quick glance, parecería que lo que se necesita para construir un Maven Project del pom de lectura. Es probable que esto implique una serie de pasos que incluyen la resolución del proyecto principal del POM, la descarga y el análisis de otros artefactos del complemento Maven y el cableado de todas las referencias.

Leer solo el pom a nivel de niño no lo hará.

+0

¡Gracias! Le daré un vistazo. Por cierto, deberías verificar que [servidor apache] (http://edwinbuck.com/) tuyo ;-) –

+0

@ TonyLâmpada Buena suerte, y que el servidor Apache tiene un propósito, si conoces las URL correctas. :) Simplemente no es un sitio web de vanidad, si eso era lo que esperabas. –

+0

ayuda: effective-pom no me ayuda, es al revés: utiliza el modelo ya construido que obtiene de project.getModel(). Pero ese modelo ya estaba prefabricado en otro lugar. –

1

el complemento de ayuda maven hace algo similar cuando se ejecuta "mvn help: effective-pom".

ver 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 para las fuentes.

Creo que esto no mostrará las condiciones transitorias.

La dependencia mvn: Meta del árbol hace eso: 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

tal vez usted puede crear una mezcla de ambos?

+0

¡Gracias, también echaré un vistazo! –

+0

Dado que effective-pom hace eso para el Proyecto maven actual, por lo general no ayuda tanto como podría pensar. effective-pom supone que el framework maven normalmente existente ya ha hecho parte del trabajo. Lo que se necesita es más un segundo proyecto de maven, y lo suficiente para ponerlo en un pom completamente resuelto. –

3

Quizás sea demasiado tarde para usted pero si puede ayuda a otros en el futuro Así que lo hice así:

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

y eso es todo. Deberia de funcionar. Si tiene algún paquete especial (por ejemplo, paquete) ... en el proyecto pom objetivo, asegúrese de que los complementos asociados a esos paquetes estén instalados en su proyecto actual.

+0

¡Gracias, me salvaste el día! :-) – eerriicc

+0

@ romain-gilles ¿qué versión de las dependencias usas? Parece que MavenProjectBuilder está en desuso en 3.x, pero RepositorySystem no está disponible en 2.2.1. En 3.x ProjectBuilder está disponible, pero no puede construir MavenProject –

+0

Encontré la respuesta a mi pregunta, detalles debajo de http://stackoverflow.com/a/39609354/157591 –

0

Por si acaso alguien lo quiere, he aquí un ejemplo que se ejecuta en Groovy. Utiliza la uva para cargar dinámicamente las dependencias necesarias para consumir el pom.xml. Carga tanto el classpath de tiempo de ejecución como el classpath de prueba.

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

Romain proporcionó la buena respuesta above, pero estaba usando una clase obsoleto que fue retirado de experto 3.x La versión actualizada es la siguiente:

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

} 

Un ejemplo de trabajo se encuentra en la hierarchy-maven-plugin

+0

¿Cómo puedo inyectar 'RepositorySystem'' ProjectBuilder' y 'MavenSession' en mi propio' Component' – DarVar

+0

'@Component (role = MyResolver.class, instanciationStrategy =" per-lookup ") public class MyResolver {....}' – DarVar

Cuestiones relacionadas