Reputation: 804
I have two Maven plugins, A
and B
. They are developed independently but they happen to need the same configurations options. Most of the potential users will have plugin A
installed, and plugin B
is a complement of it.
I'm trying to look for a way to share the configuration across the plugins. I'm aware that you can hoist up the configuration options to properties and then reuse them for every plugin, but that still requires the users to have a configuration entry per options per plugin, which is kind of boilerplate-y.
The goal of this question is to find a way to share configurations across plugins causing the less possible redundancy. Is this possible?
Upvotes: 1
Views: 161
Reputation: 804
At first, I thought this wasn't possible but by looking deeper into the internals of Maven, I realised that there was a solution. This solution is simple -- we don't have to reimplement parts of the Maven internals ourselves, we can just reuse the underlying APIs.
The trick is to use the Maven internals to modify the configuration of the Mojo that needs the configuration of another Mojo (in this case, B
needs the configuration from A
).
For that, first we need to have a mojo definition for B
that has all the configuration values (parameter
s) accepted by A
. Then, we will define the four following extra fields in B
(if they are not already defined in A
) since they will be required for the actual implementation:
@Mojo(name = "B", requiresProject = true) // and the rest of the config
public class B extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
private MojoExecution mojoExecution;
@Component
private MavenPluginManager mavenPluginManager;
// and the rest of the properties here...
public void execute() throws MojoExecutionException {
B initializedMojo = ScalaImplementation.apply(project, session, mojoExecution, mavenPluginManager, encoding);
// Implement plugin logic with `initializedMojo` instead of `this`.
}
}
With these four new fields, we can implement ScalaImplementation
in the following way (I chose to implement it in Scala code):
object ScalaImplementation {
def apply(project: MavenProject,
session: MavenSession,
mojoExecution: MojoExecution,
mavenPluginManager: MavenPluginManager): B = {
val currentConfig = mojoExecution.getConfiguration()
val pluginA = Option(project.getBuild().getPluginsAsMap().get("groupIdA:artifactIdA"))
.getOrElse(sys.error(s"Plugin A could not be found."))
val configA = pluginA.getConfiguration().asInstanceOf[Xpp3Dom]
mojoExecution.setConfiguration(Xpp3Dom.mergeXpp3Dom(currentConfig, configA))
val initializedMojoB = mavenPluginManager
.getConfiguredMojo(classOf[Mojo], session, mojoExecution)
.asInstanceOf[B] // Note that this is safe.
initializedMojoB
}
}
Note that for this solution to work, mojo B must not have any alternative required field that is not in A and has no default value (otherwise Maven will complain that it cannot initialize it to null
).
What we've done is to trick the Maven mojo engine by combining the configurations of plugin A
and plugin B
, which are accessible at runtime. First, we got a semi-initialized mojo with only the fields we care about. We then passed it to a relatively simple method that takes care of setting the configuration of the current mojoExecution
, and we instantiated a new mojo B
. Since this new mojo had the configuration values of both A
and B
, this time is fully initialized and ready to be used.
Upvotes: 1