Is it possible to use a map of maps as a Maven plugin parameter?, e.g.
@Parameter
private Map<String, Map<String, String>> converters;
and then to use it like
<converters>
<json>
<indent>true</indent>
<strict>true</strict>
</json>
<yaml>
<stripComments>false</stripComments>
</yaml>
<converters>
If I use it like this, converters
only contain the keys json
and yaml
with null as values.
I know it is possible to have complex objects as values, but is it also somehow possible to use maps for variable element values like in this example?
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 (invokingfromExpression
), 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 isMap<String, String>
). I filed the bug 498757 on the Bugzilla of this project to track this.Using a custom wrapper object
One workaround would be to not use a
Map<String, String>
as value but use a custom object:with a class
Converter
, located in the same package as the Mojo, being:You can then configure your Mojo with:
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
and the output was the expected
{json={indent=true, strict=true}, yaml={stripComments=false}}
.Using a custom configurator
I also found a way to keep a
Map<String, Map<String, String>>
by using a customComponentConfigurator
.So we want to fix
MapConverter
by inhering it, the trouble is how to register this newFixedMapConverter
. By default, Maven uses aBasicComponentConfigurator
to configure the Mojo and it relies on aDefaultConverterLookup
to look-up for converters to use for a specific class. In this case, we want to provide a custom converted forMap
that will return our fixed version. Therefore, we need to extend this basic configurator and register our new converter.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: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<implementation>
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:The configurator passed here corresponds to the role-hint specified in the
components.xml
above.With such a set-up, you can finally declare
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-pastesMapConverter
because we can't override the faulty method):One solution is quite simple and works for 1-level nesting. A more sophisticated approach can be found in the alternative answer which possibly also allows for deeper nesting of Maps.
Instead of using an interface as type parameter, simply use a concrete class like
TreeMap
The reason is this check in MapConverter which fails for an interface but suceeds for a concrete class:
As a side-note, an as it is also related to this answer for Maven > 3.3.x it also works to install a custom converter by subclassing
BasicComponentConfigurator
and using it as a Plexus component.BasicComponentConfigurator
has theDefaultConverterLookup
as a protected member variable and is hence easily accessible for registering custom converters.