Maven Shade Plugin is very common to share an application, including CDI applications. If you are used to my blog, you probably know I often use TomEE/Meecrowave/OpenWebbeans SE shades for demo and standalone applications.

However, there is a small issue you regularly hit coming from an application to Maven Shade plugin: the beans.xml management.

As a quick reminder, the beans.xml in CDI is used to configure how the archive containing this file will be scanned. It means that moving from N (>1) to 1 single archive you can have surprises.

That said, most of the time we are lucky and it is easily mergeable and mainly a matter of merging them all.

For such cases you can simply configure the XmlAppendingTransformer:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.2.4</version>
  <executions>
    <execution>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <shadedArtifactAttached>true</shadedArtifactAttached>
        <shadedClassifierName>fat</shadedClassifierName>
        <createDependencyReducedPom>false</createDependencyReducedPom>
        <dependencyReducedPomLocation>${project.build.directory}/reduced-pom.xml</dependencyReducedPomLocation>

        <transformers>
          <!-- 1 -->
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.github.rmannibucau.cdi.MyMain</mainClass>
          </transformer>

          <!-- 2 -->
          <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>

          <!-- 3 -->
          <transformer implementation="org.apache.maven.plugins.shade.resource.properties.OpenWebBeansPropertiesTransformer"/>

          <!-- 4 -->
          <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
            <resource>META-INF/beans.xml</resource>
          </transformer>
        </transformers>
        <filters>
          <filter> <!-- 5 -->
            <artifact>*:*</artifact>
            <excludes>
              <exclude>META-INF/*.SF</exclude>
              <exclude>META-INF/*.DSA</exclude>
              <exclude>META-INF/*.RSA</exclude>
              <exclude>META-INF/LICENSE.txt</exclude>
              <exclude>META-INF/LICENSE</exclude>
              <exclude>META-INF/NOTICE.txt</exclude>
              <exclude>META-INF/NOTICE</exclude>
              <exclude>META-INF/MANIFEST.MF</exclude>
              <exclude>META-INF/DEPENDENCIES</exclude>
              <exclude>module-info.class</exclude>
            </excludes>
          </filter>
        </filters>
      </configuration>
    </execution>
  </executions>
</plugin>

This common configuration will work most of the time but has some key points:

  1. Since we want an executable jar we set a main class in out shaded jar manifest,
  2. We merge ServiceLoader implementation registrations,
  3. We merge Apache OpenWebBeans configuration in a single file respecting overrides (if not set the merge will be almost random from your perspective). Optionally you can add the Microprofile config properties merger too which does more or less the same but putting config there and not as default in the code has very few value in general.
  4. And here is our beans.xml magic, we merge all the descriptors in one.

This trick will work if all descriptors have more or less the same shape since the XmlAppendingTransformer does not have any merge logic and is not able, for instance, to concatenate the <scan> configurations of the beans.xml.

For these cases where the simple extraction of the beans.xml does not work because some jar has conflicting or advanced configuration, you can still put a beans.xml in our module hosting the shade plugin declaration and exclude all others beans.xml in filters.

Last note is that if you often do that and want to automate it, you can do a pull request Apache Maven Shade Plugin to implement a CDIBeansXmlTransformer which would merge semantically the different descriptor. Once done it can look like:

<transformer implementation="org.apache.maven.plugins.shade.resource.CDIBeansXmlTransformer">
  <mergeScan>true</mergeScan>
  <version>2.0</version>
  <beanDiscoveryMode>all</beanDiscoveryMode>
  <trim>true</trim>
</transformer>

In other words, you can have a default behavior which is merging "as possible" the descriptors and enable to configure the transformer to resolve conflicting cases.

From the same author:

In the same category: