Is it possible to use a map of maps as a Maven plugin parameter?, e.g.
@Parameter
private Map
This is apparently a limitation of the sisu.plexus project internally used by the Mojo API. If you peek inside the MapConverter source, you'll find out that it first tries to fetch the value of the map by trying to interpret the configuration as a String (invoking fromExpression
), and when this fails, looks up the expected type of the value. However this method doesn't check for parameterized types, which is our case here (since the type of the map value is Map
). I filed the bug 498757 on the Bugzilla of this project to track this.
One workaround would be to not use a Map
as value but use a custom object:
@Parameter
private Map converters;
with a class Converter
, located in the same package as the Mojo, being:
public class Converter {
@Parameter
private Map properties;
@Override
public String toString() { return properties.toString(); } // to test
}
You can then configure your Mojo with:
true
true
false
This configuration will correctly inject the values in the inner-maps. It also keeps the variable aspect: the object is only introduced as a wrapper around the inner-map. I tested this with a simple test mojo having
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info(converters.toString());
}
and the output was the expected {json={indent=true, strict=true}, yaml={stripComments=false}}
.
I also found a way to keep a Map
by using a custom ComponentConfigurator
.
So we want to fix MapConverter
by inhering it, the trouble is how to register this new FixedMapConverter
. By default, Maven uses a BasicComponentConfigurator
to configure the Mojo and it relies on a DefaultConverterLookup
to look-up for converters to use for a specific class. In this case, we want to provide a custom converted for Map
that will return our fixed version. Therefore, we need to extend this basic configurator and register our new converter.
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.configurator.BasicComponentConfigurator;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.ConfigurationListener;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.configuration.PlexusConfiguration;
public class CustomBasicComponentConfigurator extends BasicComponentConfigurator {
@Override
public void configureComponent(final Object component, final PlexusConfiguration configuration,
final ExpressionEvaluator evaluator, final ClassRealm realm, final ConfigurationListener listener)
throws ComponentConfigurationException {
converterLookup.registerConverter(new FixedMapConverter());
super.configureComponent(component, configuration, evaluator, realm, listener);
}
}
Then we need to tell Maven to use this new configurator instead of the basic one. This is a 2-step process:
Inside your Maven plugin, create a file src/main/resources/META-INF/plexus/components.xml
registering the new component:
org.codehaus.plexus.component.configurator.ComponentConfigurator
custom-basic
package.to.CustomBasicComponentConfigurator
Note a few things: we declare a new component having the hint "custom-basic"
, this will serve as an id to refer to it and the
refers to the fully qualified class name of our configurator.
Tell our Mojo to use this configurator with the configurator attribute of the @Mojo
annotation:
@Mojo(name = "test", configurator = "custom-basic")
The configurator passed here corresponds to the role-hint specified in the components.xml
above.
With such a set-up, you can finally declare
@Parameter
private Map> converters;
and everything will be injected properly: Maven will use our custom configurator, that will register our fixed version of the map converter and will correctly convert the inner-maps.
Full code of FixedMapConverter
(which pretty much copy-pastes MapConverter
because we can't override the faulty method):
public class FixedMapConverter extends MapConverter {
public Object fromConfiguration(final ConverterLookup lookup, final PlexusConfiguration configuration,
final Class> type, final Type[] typeArguments, final Class> enclosingType, final ClassLoader loader,
final ExpressionEvaluator evaluator, final ConfigurationListener listener)
throws ComponentConfigurationException {
final Object value = fromExpression(configuration, evaluator, type);
if (null != value) {
return value;
}
try {
final Map