Search code examples
mavenmaven-shade-pluginuberjar

pluginManagement interferes with shade plugin


Just started with Maven for real; got a big surprise right away.

I understand (or I think I do) the concept of fat jar/uberjars. Package your code with all the dependencies, etc. maven-shade-plugin, found docs, some example, checked that it works. Now adding it to my POC project, which came from the maven-archetype-quickstart - what could possibly go wrong, eh?

To put it short, quickstart arrange the the following way:

<build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
         <plugin>
          ...

So I just threw in a plugin for shading, ready to call it a day:

<!-- Maven Shade Plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <!-- Run shade goal on package phase -->
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>

Not so fast. First, I noticed that mvn clean package didn't mention maven-shade-plugin being executed (like the working example did). Hours of searching followed, and I was staring the "root cause" in the face - <pluginManagement>, offered by quickstart archetype, was it - somehow. Removing that tag magically allowed maven.shade.plugin to do its job. Otherwise, nope.

(Replica: https://github.com/alexakarpov/shade-me unshade is the bad branch, master is good).

Talk about violation of the Principle of Least Surprise, eh =) Can someone explain what's going on? The comment in generated pom mentioned something about parent pom, but I'm not doing anything with multi-pom setup yet..


Solution

  • <pluginManagement> role is described in Maven documentation :

    Plugin Management contains plugin elements in much the same way [than plugins], except that rather than configuring plugin information for this particular project build, it is intended to configure project builds that inherit from this one.

    Its goal here in the project generated by the archetype is to set specified versions of default plugins (maven-clean-plugin, maven-jar-plugin, ...). Note that these default plugins do not appear in your POM <plugins> section, but they are declared implicitly (you can check it by running mvn help:effective-pom).

    But adding a plugin to <pluginManagement> section does not make your project invoke that plugin. Here, you can just set configuration and the version of the plugin you want to use. To invoke the plugin, you should absolutely declare it in <plugins> section.

    In some projects (most of time multi-module projects), you could see the plugin and its configuration declared in <pluginManagement> of parent POM, and then referenced on <plugins> section of modules needing invocation of that plugin : thus, you do not have to repeat the same configuration on each module.

    <pluginManagement> is mostly used if you want to use POM inheritance. Otherwise, on simple projects, you can just declare them in <plugins> section. I've also seen some projects defining all configuration in <pluginManagement>, just to make <plugins> section shorter and more readable, like the following example. It's just a matter of taste.

    <build>
        <!-- pluginManagement section : set versions and configurations -->
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.3</version>
                    <executions>
                        <!-- Run shade goal on package phase -->
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <transformers>
                                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    </transformer>
                                </transformers>
                            </configuration>
                        </execution>
                    </executions>
                  </plugin>
            </plugins>
        </pluginManagement>
    
        <!-- plugins section : plugins that are invoked when building the project -->
        <plugins>
            <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    

    You can also read more on StackOverflow : Maven : What is pluginManagement?