2

I have a Maven extension that I've set up to be configured from a POM configuration object where

<configuration>
  <foo>...</foo>
  <bar>...</bar>
</configuration>

ends up calling

setFoo(...);
setBar(...);

methods.

I'd like to allow configurations to import extra configuration to allow a propose&second style of delegation, so

<configuration>
  <import>g:a:v</import>
  <bar>...</bar>
</configuration>

where the artifact g:a:v has a file META-INF/my-project-name.xml with content <configuration><foo>...</foo></configuration>.

I'd like the combination of the <configuration> with the import and that XML file to generate the same calls to setters as above.


Using

import org.codehaus.plexus.component.configurator.ComponentConfigurator;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;

with

<dependency>
  <groupId>org.codehaus.plexus</groupId>
  <artifactId>plexus-container-default</artifactId>
  <version>1.6</version>
</dependency>
<dependency>
  <groupId>org.codehaus.plexus</groupId>
  <artifactId>plexus-classworlds</artifactId>
  <version>2.5.2</version>
</dependency>

I've written the following to try and parse configuration from an XML file and then invoke the configurator to configure my Maven extension.

{
  PlexusConfiguration configuration;
  try {
    configuration = loadConfiguration(
        log, cr.get(), EXTRA_CONFIGURATION_XML_RELATIVE_PATH);
  } catch (IOException ex) {
    throw new EnforcerRuleException(
        "Failed to load " + EXTRA_CONFIGURATION_XML_RELATIVE_PATH
        + " from " + artifactId, ex);
  }

  // TODO: is this right.
  // Newer versions have a MavenProject.getClassRealm() says
  // """
  // Warning: This is an internal utility method that is only public for
  // technical reasons, it is not part of the public API. In particular,
  // this method can be changed or deleted without prior notice and must
  // not be used by plugins.
  // """
  ClassRealm realm = null;

  try {
    configurator.configureComponent(configurable, configuration, realm);
  } catch (ComponentConfigurationException ex) {
    throw new EnforcerRuleException(
        "Failed to process configuration "
        + EXTRA_CONFIGURATION_XML_RELATIVE_PATH
        + " from " + artifactId,
        ex);
  }
}

where configurable is just the Object with setters and configuration is an XmlPlexusConfiguration loaded thus:

static XmlPlexusConfiguration loadConfiguration(
    Log log,
    ClassRoot cr,
    String path)
throws EnforcerRuleException, IOException {
  log.debug("Loading " + path + " from " + cr.art.getId());
  File classRootFile = cr.classRoot;
  if (classRootFile == null) {
    throw new EnforcerRuleException(
        "Cannot import configuration from unresolved artifact "
        + art.getId());
  }
  Xpp3Dom dom = cr.readRelativePath(
      path,
      new ClassRoot.IOConsumer<InputStream, Xpp3Dom>() {
        public Xpp3Dom read(InputStream is) throws IOException {
          try {
            return Xpp3DomBuilder.build(is, "UTF-8", true);
          } catch (XmlPullParserException ex) {
            throw new IOException("Malformed XML", ex);
          } finally {
            is.close();
          }
        }
      });
  return new XmlPlexusConfiguration(dom);
}

I acquire the ComponentConfigurator via

  configurator = (ComponentConfigurator) helper.getComponent(
      ComponentConfigurator.class);

When I run this I get,

org.codehaus.plexus.component.configurator.ComponentConfigurationException:
Component does not implement interface org.codehaus.plexus.component.MapOrientedComponent
    at org.codehaus.plexus.component.configurator.MapOrientedComponentConfigurator.configureComponent(MapOrientedComponentConfigurator.java:41)
    at org.codehaus.plexus.component.configurator.AbstractComponentConfigurator.configureComponent(AbstractComponentConfigurator.java:44)
    at org.codehaus.plexus.component.configurator.AbstractComponentConfigurator.configureComponent(AbstractComponentConfigurator.java:37)
    at com.google.security.fences.ConfigurationImport.configure(ConfigurationImport.java:70)
    at com.google.security.fences.FencesMavenEnforcerRule.execute(FencesMavenEnforcerRule.java:146)
    at org.apache.maven.plugins.enforcer.EnforceMojo.execute(EnforceMojo.java:193)

Is there some way to bridge MapOrientedComponents and the bean-style reflective setter invocation used to configure my extension?

Or is there a better way to turn the text of an XML file into additional configuration operations?


EDIT:

After a bit more digging it looks like

configurator = (ComponentConfigurator) helper.getComponent(
    ComponentConfigurator.class);

returns a MapOrientedComponentConfigurator when I run it via the integration-test Verifier causing the failure, but when I do not, it produces a different & compatible kind of configurator.

The difference, 100% repeatable, is whether I run with -X or not to cause the logger to produce debug trace.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245

1 Answers1

1

I don't know the root-root cause, but I do know that

  1. When running with -X to turn on debugging, helper.getComponent(ComponentConfigurator.class) returns a BasicComponentConfigurator and everything works fine.
  2. When running without -X, the same call returns a MapOrientedComponentConfigurator which barfs since it can only configure MapOrientedComponents.
  3. Replacing that call with new BasicComponentConfigurator() works.
  4. Working around dependency injection makes me feel dirty.

My best guess is that depending on whether debugging trace is requested, something is creating a component configurator in some scope and not cleaning up after itself.


https://github.com/mikesamuel/fences-maven-enforcer-rule/commit/7cab8d8bd873f2341acab088e5bdad9c3e35640b is the commit at which this behavior was "fixed."

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245