status: done in Maven Plugin Tools 3.0
Context
Maven plugins are described in META-INF/maven/plugin.xml descriptor, which is in general generated by maven-plugin-plugin. Information for the generator are written as javadoc annotations in java source: see maven-plugin-tools-java.
Using java 5 annotations instead of javadoc ones have multiple benefits:
- compile-time checks for plugin metadata, with enums for some annotations
- inheritance support
- annotations are supported in most IDEs, providing code-completion and syntactic checks
see plexus-component-annotations for an example of such a work done on Plexus.
Existing implementations
Multiple implementations of such annotations exist:
- JFrog's Maven Anno Mojo: see the wiki or annotations source,
the idea is having as much annotations as actual javadoc annotations, each with only 1 attribute - Maven MNG-2521, with its proposal from 2007 providing a source branch and its annotations,
the idea is having only 4 annotations each with multiple attributes: Goal + Execute and Parameter + Component
Proposal
Continue the work started on MNG-2521 before plugin-tools extraction from Maven Core.
Annotations:
- 4 annotations: @Mojo and @Execute at class-level, @Parameter and @Component at field level
- java package: org.apache.maven.tools.plugin.annotations (consistent with maven-plugin-tools-api
- a new maven-plugin-tools-annotations component in plugin-tools
Extractor: addition into existing maven-plugin-tools-java
New features
- use annotations from parents classes coming from reactors and external dependencies.
Implementation
Initial work done in trunk http://svn.apache.org/repos/asf/maven/plugin-tools/trunk/
The maven-plugin-plugin version has been bump to 3.0-SNAPSHOT.
State of the development as of 22/5/2012
Following usage for a plugin developer is working, with annotations near previous javadoc tags:
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Execute; import org.apache.maven.plugins.annotations.InstanciationStrategy; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; /** * Mojo Description. @Mojo( name = "<goal-name>" ) is the minimal required annotation. * @since <since-text> * @deprecated <deprecated-text> */ @Mojo( name = "<goal-name>", aggregator = <false|true>, configurator = "<role-hint>", executionStrategy = "<once-per-session|always>", inheritByDefault = <true|false>, instantiationStrategy = InstanciationStrategy.<strategy>, defaultPhase = "<phase-name>", requiresDependencyResolution = ResolutionScope.<scope>, requiresDependencyCollection = ResolutionScope.<scope>, // (since Maven 3.0) requiresDirectInvocation = <false|true>, requiresOnline = <false|true>, requiresProject = <true|false>, requiresReports = <false|true>, // (unsupported since Maven 3.0) threadSafe = <false|true> ) // (since Maven 3.0) @Execute( goal = "<goal-name>", phase = LifecyclePhase.<phase> lifecycle = "<lifecycle-id>" ) public class MyMojo extends AbstractMojo { /** * @since <since-text> * @deprecated <deprecated-text> */ @Parameter( alias = "myAlias", property = "a.property", defaultValue = "an expression with ${variables} eventually", readonly = <false|true> required = <false|true> ) private String parameter; /** * @since <since-text> * @deprecated <deprecated-text> */ @Component( role = MyComponentExtension.class, hint = "..." ) private MyComponent component; public void execute() { ... } }
Improvement Idea #1
rename roleHint to hint to match Plexus @Requirement
@Component( role = MyComponentExtension.class, hint = "..." ) private MyComponent component;
DONE
Improvement Idea #2
Remove readonly attribute from @Parameter (was used for Maven components like ${project} to mark that it should not be configured by plugin user), and replace by a new attribute of @Component which is mutually exclusive with role+hint
@Component( "project" ) private MavenProject project; @Component( role = MyComponentExtension.class, roleHint = "..." ) private MyComponent component;
evaluation: ${project.compileClasspathElements} is an expression that would be readonly but doesn't really resolve to a component, so the term isn't appropriate. Need a better term.
Improvement Idea #3
Introduce JSR-330, by requiring plugin developer to mark injected fields with standard @Inject and make @Parameter and @Component standard Qualifier.
Costs more writing for plugin developer (these @Inject) without much win other than mark the intent in a standard way (and shouldn't change much for maven-plugin-tools)
/** * @since <since-text> * @deprecated <deprecated-text> */ @Inject @Parameter( alias = "myAlias", property = "aProperty", defaultValue = "${anExpression}", readonly = <false|true> required = <false|true> ) private String parameter; /** * @since <since-text> * @deprecated <deprecated-text> */ @Inject @Component( role = MyComponentExtension.class, roleHint = "..." ) private MyComponent component;
evaluation: since plugin.xml is still generated and content is injected by Maven core using this plugin.xml and not annotations taken from bytecode, this is not really useful and causes more confusion/harm than benefit
5 Comments
Stephen Connolly
I think it is a bad smell to have annotations like @Mojo, @MojoExecute, @MojoParameter and @MojoComponent
We should take use of the whole package namespacing thingy... no need to prefix with Mojo IMHO
@Goal
@Execute
@Parameter
@Component
are better, e.g.
is better than
Now I would see @Mojo being appropriate if we were ditching inheritance for mojo's, so that if you had
But actually, I would prefer in that case
I think the above could be implemented somewhat using synthentic bridging classes generated during the annotation processing
Stephen Connolly
Actually thinking about my final example, there is no need for the @Mojo annotation on the class in that case at all!
Herve Boutemy
ok, no "Mojo" prefix for Execute, Parameter and Component
for the multiple goals in a single class, yes, a future feature can be added when @Mojo is used as a method annotation
I only see one issue to implement this feature: annotations are available after compilation, but such a feature would require generating source, then before compilation...
I don't think that this feature qill be available for the first release, but we can work on it after if really requested
Stephen Connolly
Well I was thinking that using ASM you could just generate the synthetic bridging classes in the process-classes phase. The bytecode would be simple as it will always do the exact same
Stephen Connolly
Ahh yes... I'm seeing the chicken and egg issue....
Of course the chicken and egg issue also exists for the help mojo that gets generated...