Jachk E Jachk E - 1 month ago 6
Java Question

Why is Maven not resolving jetty:run, when it is correctly resolving findbugs:findbugs?

I was doing some testing using maven. And I realize that I can execute the findbugs goal of the findbugs plugin without adding the plugin to the pom file. On the other hand, when I needed to run the run goal of the jetty plugin I was obligated to add the plugin to the pom file or the build fail.

So I have two questions :


  • Why jetty needed configuration in pom while findbugs didn't?

  • How does maven know which findbugs to execute (suppose we have to plugins with the same name but different groupid) ?



When I run the first command the build is successful without any changes in pom file:

mvn findbugs:findbugs
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building module-mytest 1.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- findbugs-maven-plugin:3.0.4:findbugs (default-cli) @ module-mytest ---
[INFO] Fork Value is true
[java] Warnings generated: 6
[INFO] Done FindBugs Analysis....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.165s
[INFO] Finished at: Sun Oct 23 18:40:26 WEST 2016
[INFO] Final Memory: 21M/111M
[INFO] -----------------------------------------------------------------------


But When I run the second I get this :

mvn jetty:run
[INFO] Scanning for projects...
Downloading: http://repo.maven.apache.org/maven2/org/codehaus/mojo/maven-metadata.xml
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml (13 KB at 30.0 KB/sec)
Downloaded: http://repo.maven.apache.org/maven2/org/codehaus/mojo/maven-metadata.xml (20 KB at 41.0 KB/sec)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.129s
[INFO] Finished at: Sun Oct 23 18:43:27 WEST 2016
[INFO] Final Memory: 12M/104M
[INFO] ------------------------------------------------------------------------
[ERROR] No plugin found for prefix 'jetty' in the current project and in the plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from the repositories [local (/home/hp-pc/.m2/repository), central (http://repo.maven.apache.org/maven2)] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/NoPluginFoundForPrefixException


So in order to pass the build I needed to add the following to the pom file:

<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.11.v20150529</version>
</plugin>

Answer

What is a prefix and why do we need it?

You just encountered the Plugin Prefix Resolution of Maven. This is a feature which enables the user to invoke goals of a specific Maven plugin, by using its prefix. When you invoke directly a goal on the command-line, you could use the fully-featured form of:

mvn my.plugin.groupId:foo-maven-plugin:1.0.0:bar

This would invoke the goal bar of the Foo Maven plugin having the coordinates my.plugin.groupId:foo-maven-plugin:1.0.0 (in the form of groupId:artifactId:version). It works well, but it is a bit verbose. It would be nice to invoke this goal in a simpler manner, without specifying all those coordinates. Maven makes this possible by assigning prefixes to plugins, so that you can refer to this prefix instead of the whole coordinates, with:

mvn foo:bar
    ^^^ ^^^
     |    |
   prefix |
          |
         goal

How is this prefix determined?

You can define a prefix for each Maven plugin. This corresponds to a simple name used to identify it:

The conventional artifact ID formats to use are:

  • maven-${prefix}-plugin - for official plugins maintained by the Apache Maven team itself (you must not use this naming pattern for your plugin, see this note for more informations)
  • ${prefix}-maven-plugin - for plugins from other sources

If your plugin's artifactId fits this pattern, Maven will automatically map your plugin to the correct prefix in the metadata stored within your plugin's groupId path on the repository.

Put another way, if your plugin's artifact id is named foo-maven-plugin, Maven will automatically assign it a prefix of foo. If you don't want this default assignment, you can still configure your own with the help of the maven-plugin-plugin and its goalPrefix parameter.

How does Maven map prefixes to plugins?

In the command

mvn foo:bar

Maven must have a way to deduce that foo actually means my.plugin.groupId:foo-maven-plugin. In the settings.xml file, you can add plugin groups, in the form of:

<pluginGroups>
  <pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>

What this does, is telling Maven which group id it is supposed to consider when you're using a prefix in a command. By default, and in addition to the groups specified in the settings, Maven also searches the group ids org.apache.maven.plugins and org.codehaus.mojo. It searches those default ones after the ones you configured in the settings. Therefore, with the configuration above, and a command of mvn foo:bar, Maven will look for a plugin having a prefix of foo inside the group id org.mortbay.jetty, org.apache.maven.plugins and org.codehaus.mojo.

The second step is how that search is actually performed. Maven will download metadata files (or look them into your local repository if they are already downloaded), called maven-metadata.xml, from each remote repositories at those group ids. If we take the example where the only remote repository we have in Maven Central, Maven will first download http://repo1.maven.org/maven2/org/mortbay/jetty/maven-metadata.xml, and look inside this file if we have something mapping foo. Notice how the group id was transformed into a directory structure in the remote repository. The structure of this metadata file is:

<metadata>
  <plugins>
    <plugin>
      <name>Some Awesome Maven Plugin</name>
      <prefix>somePrefix</prefix>
      <artifactId>some-maven-plugin</artifactId>
    </plugin>
  </plugins>
</metadata>

If none of the <plugin> section contain a <prefix> that is equal to the one we specified (foo), Maven will continue with the next group id, hitting http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml. Again, if none are found, Maven will finally hit http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml. If none are still found, there is nothing Maven can do for you anymore, and it will error:

[ERROR] No plugin found for prefix 'foo' in the current project and in the plugin groups [org.mortbay.jetty, org.apache.maven.plugins, org.codehaus.mojo] available from the repositories [local (.../.m2/repository), central (http://repo.maven.apache.org/maven2)] -> [Help 1]

This is the error you have here. However, if one match was made during this search, then Maven can deduce the <artifactId> to use. It now means it has the group id, and the artifact id. The final piece of the puzzle is the version, and for that, Maven will take the latest one available, unless you specified a specific version on the command line with -Dversion=1.0.0.

We now have gathered the group id, the artifact id, and the version; time to finally invoke the bar goal of our plugin.

What's the issue with my configuration?

As said above, Maven looks in certain pre-defined group ids inside the active remote repositories to look for matches with a given prefix. With the command

mvn findbugs:findbugs

Maven starts the search with the findbugs prefix. Since our configuration does not have any <pluginGroup> in our settings, Maven looks into org.codehaus.mojo and org.apache.maven.plugins group id for a prefix match.

And it does find one: Findbugs Maven Plugin is published under the org.codehaus.mojo group id; indeed, you can find it in the maven-metadata.xml:

<plugin>
  <name>FindBugs Maven Plugin</name>
  <prefix>findbugs</prefix>
  <artifactId>findbugs-maven-plugin</artifactId>
</plugin>

So the resolution succeeded, and then Maven can continue to invoke the findbugs goal of this plugin.

The second example is the command

mvn jetty:run

As before, the same resolution steps happen, but, in this case, you'll find out that the prefix <jetty> does not appear in any of the maven-metadata.xml for the group ids org.codehaus.mojo and org.apache.maven.plugins. So the resolution fails, and Maven returns the error that you have.

But we've seen how to make it work! We can add a <pluginGroup> inside our settings, so that this group id can also be searched during resolution. The Jetty Maven Plugin is published under the group id org.eclipse.jetty, and if we peek into the corresponding maven-metadata.xml in Maven Central, you'll see that <prefix>jetty</prefix> is there. So the fix is simple: just define this new group id to search inside the settings:

<pluginGroups>
  <pluginGroup>org.eclipse.jetty</pluginGroup>
</pluginGroups>

Now, Maven will also look into this group id, and match the jetty prefix to the org.eclipse.jetty:jetty-maven-plugin successfully.


Of course, all of this resolution can be side-tracked if you define the plugin explicitly in your POM, which is the other solution you found:

<plugin>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>9.2.11.v20150529</version>
</plugin>

If you configure the plugin directly in the POM, the prefix resolution still happens, but it is a bit masked: Maven will download the plugin from the configured remote repositories, and will download and install all metadata files along the way, including the maven-metadata.xml containing the mapping for the prefix jetty. So since it downloads it automatically, the search always succeeds.