Search code examples
javaxmlmavenaether

Can you get plugin configuration from a POM using aether?


I have a requirement to read plugin configurations in Java. I have used the aether library to get run time dependencies, compile time dependencies etc. But can I use aether to read plugin configurations based in the pom file?

Something like this

<properties>
  <servicePort>8080</servicePort>
  <adminPort>8081</adminPort>
</properties>

<build>
  <plugins>
    <plugin>
      <groupId>com.company.group</groupId>
      <artifactId>my-plugin</artifactId>
      <version>0.1-SNAPSHOT</version>
      <configuration>
        <myConfig>
          <somePropName>someProp</somePropName>
          <portMappings>
            <someProp>${servicePort}</someProp>
            <someProp-admin>${adminPort}</someProp-admin>
          </portMappings>
        </myConfig>
      </configuration>
    </plugin>
  </plugins>
</build>

I want to be able to resolve

some-Prop 8080
some-prop-admin 8081 

from this mechanism

Currently I fetch the compile tie dependencies like this

Dependency dependency = new Dependency(new DefaultArtifact(
                    coordinate), COMPILE);
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot(dependency);

collectRequest.addRepository(this.aetherSession
        .getRemoteRepository());
DependencyNode node = this.aetherSession
        .getRepoSystem()
        .collectDependencies(this.aetherSession.getRepoSession(),
                collectRequest).getRoot();
DependencyRequest dependencyRequest = new DependencyRequest();
dependencyRequest.setRoot(node);
result = this.aetherSession
        .getRepoSystem()
        .resolveDependencies(this.aetherSession.getRepoSession(),
                dependencyRequest).getArtifactResults();
FinalResult.addAll(result);

Solution

  • I don't know if there is a simpler way, but you can use the Aether API to resolve the POM artifact you're interested in, and then build a Maven model from it with the Model Builder API.

    First of all, if the coordinates to your artifact isn't to the POM artifact, you need to convert them to the POM artifact by creating a new DefaultArtifact with the same GAV, but of type "pom". To resolve the artifact, you can invoke resolveArtifact on the repository system, and retrieve the result with getArtifact().

    Once you have the resolved artifact, you can use maven-model-builder to build the Maven model from the artifact's file. The ModelBuilder can be created with DefaultModelBuilderFactory.newInstance() factory method.

    For artifacts that have a parent POM, a ModelResolver needs to be set on the request to build a Model. However, the only usable implementation with Aether, which is DefaultModelResolver, is package-private inside maven-aether-provider so we need to use the Reflection API to construct it. It needs to be injected components that are retrieved with the serviceLocator returned by MavenRepositorySystemUtils.newServiceLocator(). This needs to be the same locator with which the Aether session was constructed.

    DefaultArtifact artifact = new DefaultArtifact(coordinate);
    Artifact pomArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), "pom", artifact.getVersion());
    
    ArtifactRequest request = new ArtifactRequest(pomArtifact, Arrays.asList(aetherSession.getRemoteRepository()), null);
    pomArtifact = aetherSession.getRepoSystem().resolveArtifact(session, request).getArtifact();
    
    ModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
    ModelBuildingRequest buildingRequest = new DefaultModelBuildingRequest();
    buildingRequest.setPomFile(pomArtifact.getFile());
    buildingRequest.setProcessPlugins(true);
    buildingRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
    
    Constructor<?> constr = Class.forName("org.apache.maven.repository.internal.DefaultModelResolver").getConstructors()[0];
    constr.setAccessible(true);
    ModelResolver modelResolver = (ModelResolver) constr.newInstance(session, null, null,
            serviceLocator.getService(ArtifactResolver.class),
            serviceLocator.getService(VersionRangeResolver.class),
            serviceLocator.getService(RemoteRepositoryManager.class), request.getRepositories());
    buildingRequest.setModelResolver(modelResolver);
    
    Model model = modelBuilder.build(buildingRequest).getEffectiveModel();
    
    Xpp3Dom pluginConfiguration = (Xpp3Dom) model.getBuild().getPluginsAsMap().get("com.company.group:my-plugin").getConfiguration();
    Xpp3Dom myConfig = pluginConfiguration.getChild("myConfig");
    System.out.println(myConfig.getChild("somePropName").getValue()); // prints "someProp"
    

    By default, the model builder isn't configured to process plugins, so we need to invoke setProcessPlugin(true). Once the effective model is obtained, the configuration is contained in a Xpp3Dom object that you can navigate with the help of getChild(name), to get the named child XML element, and getValue() to get the value of the XML element.