CLOVER🍀

That was when it all began.

Maven Shade PluginのResource Transformerをちゃんと見る

これは、なにをしたくて書いたもの?

Maven Shade Pluginを使うと、依存関係を含めたアーティファクトをUber JARにパッケージングすることができます。

で、設定する時にResource Transformerをなんとなく使っていたので、今回ちゃんと見てみようかなぁという気になりまして。

Resource Transformer?

Resource Transformerとは、複数のアーティファクトやライブラリを集約する際に、重複するリソースをマージするための
方法を提供するものです。

一覧はここに載っています。

Apache Maven Shade Plugin – Resource Transformers

ざっと載せると、こんな感じですね。

ソースコード自体は、こちらにあります。

https://github.com/apache/maven-shade-plugin/tree/maven-shade-plugin-3.2.4/src/main/java/org/apache/maven/plugins/shade/resource

https://github.com/apache/maven-shade-plugin/tree/maven-shade-plugin-3.2.4/src/main/java/org/apache/maven/plugins/shade/resource/properties

とまあ、説明を読んでいるとどんなことができそうなのか雰囲気はわかるのですが、少しは使ってみましょうか。

お題

こちらのネタを流用して、Maven Shade Pluginを使ってUber JARを作ろうと思います。

RESTEasy+Vert.x(Handler)で遊ぶ - CLOVER🍀

ソースコードの内容は、もうちょっとシンプルにします。

この時に、Resource Transformerをいくつか使ってみましょう。

環境

今回の環境は、こちら。

$ java --version
openjdk 11.0.9.1 2020-11-04
OpenJDK Runtime Environment (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.9.1+1-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.9.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-54-generic", arch: "amd64", family: "unix"

準備

Maven依存関係は、これだけ設定。

    <dependencies>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-vertx</artifactId>
            <version>4.5.8.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-reactor</artifactId>
            <version>4.5.8.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>4.5.8.Final</version>
        </dependency>

        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
            <version>3.9.4</version>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>2.0.SP1</version>
        </dependency>
    </dependencies>

ソースコードはこちら。ちょっと強引に、Jackson 2とReactorを使います。

src/main/java/org/littlewings/maven/shade/App.java

package org.littlewings.maven.shade;

import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import org.jboss.logging.Logger;
import org.jboss.resteasy.plugins.server.vertx.VertxRequestHandler;
import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment;
import org.jboss.resteasy.spi.ResteasyDeployment;

public class App {
    public static void main(String... args) {
        Logger logger = Logger.getLogger(App.class);

        Vertx vertx = Vertx.vertx();
        HttpServer server = vertx.createHttpServer();

        ResteasyDeployment deployment = new VertxResteasyDeployment();
        deployment.start();
        deployment.getRegistry().addPerRequestResource(EchoResource.class);

        server.requestHandler(new VertxRequestHandler(vertx, deployment));

        int port = 8080;
        server.listen(port);

        logger.infof("startup, server[%d]", port);
    }
}

src/main/java/org/littlewings/maven/shade/EchoResource.java

package org.littlewings.maven.shade;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import reactor.core.publisher.Mono;

@Path("echo")
public class EchoResource {
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Mono<MessageResponse> echo(MessageRequest request) {
        String message = request.getMessage();

        String responseMessage = String.format("reply: %s", message);

        return Mono.just(new MessageResponse(responseMessage));
    }


    public static class MessageRequest {
        String message;

        public String getMessage() {
            return message;
        }
    }

    public static class MessageResponse {
        String message;

        public MessageResponse(String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }
}

起動。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.maven.shade.App

動作確認。

$ curl -i -H 'Content-Type: application/json' localhost:8080/echo -d '{"message": "Hello World!!"}'
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/json

{"message":"reply: Hello World!!"}

このアプリケーションを、Maven Shade PluginでUber JARにしていきます。

Maven Shade Pluginを設定する

なにはともあれということで、Maven Shade Pluginをまずは設定します。

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
                </configuration>
            </plugin>
        </plugins>
    </build>

パッケージングします。

$ mvn package

すると、targetディレクトリ配下に生成されるJARファイルが、Uber JARになります。

$ ll -h target/maven-shade-plugin-example-0.1.jar 
-rw-rw-r-- 1 xxxxx xxxxx 14M 11月 22 23:04 target/maven-shade-plugin-example-0.1.jar

起動には、mainメソッドを持ったクラスの指定が必要です。

$ java -cp target/maven-shade-plugin-example-0.1.jar org.littlewings.maven.shade.App
11月 22, 2020 11:05:55 午後 org.littlewings.maven.shade.App main
INFO: startup, server[8080]

実は、オリジナルのJARファイルを置き換えるようにメッセージが出ているので

[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /path/to/target/maven-shade-plugin-example-0.1.jar with /path/to/target/maven-shade-plugin-example-0.1-shaded.jar

オリジナルのJARファイルを残すようにしてみましょうか。

configurationで、shadedArtifactAttachedをtrueに指定します。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                </configuration>
            </plugin>

パッケージング。

$ mvn clean package

こうすると、オリジナルのJARファイルが残り、Maven Shade PluginによるUber JARはshaded classierが付いた状態で
作成されます。

$ ll target/*.jar
-rw-rw-r-- 1 xxxxx xxxxx 13668491 11月 22 23:07 target/maven-shade-plugin-example-0.1-shaded.jar
-rw-rw-r-- 1 xxxxx xxxxx     5395 11月 22 23:07 target/maven-shade-plugin-example-0.1.jar

このshadedというのは、shadedClassifierNameで変更可能です。

                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                    <shadedClassifierName>uber-jar</shadedClassifierName>
                </configuration>

生成される名前については、outputFile、shadedArtifactId、finalNameなど設定可能なものがいくつかあるので、詳しくは
shade:shadeゴールのドキュメントを参照。

Apache Maven Shade Plugin – shade:shade

以降は、以下の状態からconfigurationの中を変更していきます。

                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                </configuration>

ManifestResourceTransformerでMANIFESTの設定を行う

最初に、mainクラスを指定していたのをなんとかしましょう。ManifestResourceTransformerを使用します。

Setting Manifest Entries with the ManifestResourceTransformer

こちらにも、ほぼ同等のことが書かれています。

Apache Maven Shade Plugin – Executable JAR

こんな感じで。

                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <manifestEntries>
                                <Main-Class>org.littlewings.maven.shade.App</Main-Class>
                            </manifestEntries>
                        </transformer>
                    </transformers>
                </configuration>

これで、Main-Classを指定するとMETA-INF/MANIFEST.MFに指定したクラスが設定されます。

MANIFEST.MFの中身を確認。

$ unzip -p target/maven-shade-plugin-example-0.1-shaded.jar META-INF/MANIFEST.MF
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven 3.6.3
Built-By: kazuhira
Build-Jdk: 11.0.9.1
Main-Class: org.littlewings.maven.shade.App

パッケージングして起動確認。

$ java -jar target/maven-shade-plugin-example-0.1-shaded.jar 
11月 22, 2020 11:25:22 午後 org.littlewings.maven.shade.App main
INFO: startup, server[8080]

java -jarで起動できるようになりました。

ServicesResourceTransformerを使って、META-INF/servicesの中身をマージする

ところで、現状だとJARファイルを実行してもうまく動きません。

$ curl -i -H 'Content-Type: application/json' localhost:8080/echo -d '{"message": "Hello World!!"}'
HTTP/1.1 415 Unsupported Media Type
transfer-encoding: chunked

Content-Typeに対応したMessageBodyReaderがないと言っています。

11月 22, 2020 11:26:56 午後 org.jboss.resteasy.core.ExceptionHandler handleWebApplicationException
ERROR: RESTEASY002010: Failed to execute
javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: class org.littlewings.maven.shade.EchoResource$MessageRequest of content type: application/json
    at org.jboss.resteasy.core.interception.jaxrs.ServerReaderInterceptorContext.throwReaderNotFound(ServerReaderInterceptorContext.java:55)
    at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:133)
    at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:75)

パッケージングする時に実はこんな感じで大量にWARNINGが出力されていて、Uber JARを作るために各アーティファクト、
ライブラリをまとめる時にファイルが重複しているものがあると言っています。

[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] Discovered module-info.class. Shading will break its strong encapsulation.
[WARNING] asyncutil-0.1.0.jar, btf-1.3.jar, cdi-api-2.0.SP1.jar, commons-codec-1.13.jar, commons-io-2.5.jar, commons-logging-1.2.jar, httpclient-4.5.12.jar, httpcore-4.4.13.jar, istack-commons-runtime-3.0.10.jar, jackson-annotations-2.11.1.jar, jackson-core-2.11.1.jar, jackson-coreutils-2.0.jar, jackson-databind-2.11.1.jar, jackson-jaxrs-base-2.11.1.jar, jackson-jaxrs-json-provider-2.11.1.jar, jackson-module-jaxb-annotations-2.11.1.jar, jakarta.activation-1.2.1.jar, jakarta.activation-api-1.2.1.jar, jakarta.validation-api-2.0.2.jar, javax.el-api-3.0.0.jar, javax.interceptor-api-1.2.jar, jaxb-runtime-2.3.3-b02.jar, jboss-annotations-api_1.3_spec-2.0.1.Final.jar, jboss-jaxb-api_2.3_spec-1.0.1.Final.jar, jboss-jaxrs-api_2.1_spec-2.0.1.Final.jar, jboss-logging-3.3.2.Final.jar, json-patch-1.13.jar, maven-shade-plugin-example-0.1.jar, microprofile-config-api-1.4.jar, microprofile-context-propagation-api-1.0-RC1.jar, msg-simple-1.2.jar, netty-buffer-4.1.49.Final.jar, netty-codec-4.1.49.Final.jar, netty-codec-dns-4.1.49.Final.jar, netty-codec-http-4.1.49.Final.jar, netty-codec-http2-4.1.49.Final.jar, netty-codec-socks-4.1.49.Final.jar, netty-common-4.1.49.Final.jar, netty-handler-4.1.49.Final.jar, netty-handler-proxy-4.1.49.Final.jar, netty-resolver-4.1.49.Final.jar, netty-resolver-dns-4.1.49.Final.jar, netty-transport-4.1.49.Final.jar, reactive-streams-1.0.3.jar, reactor-core-3.4.0.jar, resteasy-client-4.5.8.Final.jar, resteasy-client-api-4.5.8.Final.jar, resteasy-context-propagation-4.5.8.Final.jar, resteasy-core-4.5.8.Final.jar, resteasy-core-spi-4.5.8.Final.jar, resteasy-jackson2-provider-4.5.8.Final.jar, resteasy-jaxb-provider-4.5.8.Final.jar, resteasy-reactor-4.5.8.Final.jar, resteasy-vertx-4.5.8.Final.jar, smallrye-config-1.6.1.jar, smallrye-config-common-1.6.1.jar, smallrye-context-propagation-1.0.1.jar, smallrye-context-propagation-api-1.0.1.jar, txw2-2.3.3-b02.jar, vertx-core-3.9.4.jar define 1 overlapping resource: 
[WARNING]   - META-INF/MANIFEST.MF
[WARNING] istack-commons-runtime-3.0.10.jar, jakarta.activation-1.2.1.jar, jakarta.activation-api-1.2.1.jar, jaxb-runtime-2.3.3-b02.jar, jboss-annotations-api_1.3_spec-2.0.1.Final.jar, jboss-jaxrs-api_2.1_spec-2.0.1.Final.jar, txw2-2.3.3-b02.jar define 2 overlapping resources: 
[WARNING]   - META-INF/LICENSE.md
[WARNING]   - META-INF/NOTICE.md
[WARNING] cdi-api-2.0.SP1.jar, commons-codec-1.13.jar, commons-io-2.5.jar, commons-logging-1.2.jar, javax.el-api-3.0.0.jar, javax.interceptor-api-1.2.jar, jboss-jaxb-api_2.3_spec-1.0.1.Final.jar, jboss-logging-3.3.2.Final.jar define 1 overlapping resource: 
[WARNING]   - META-INF/LICENSE.txt
[WARNING] btf-1.3.jar, httpclient-4.5.12.jar, httpcore-4.4.13.jar, jackson-annotations-2.11.1.jar, jackson-core-2.11.1.jar, jackson-coreutils-2.0.jar, jackson-databind-2.11.1.jar, jackson-jaxrs-base-2.11.1.jar, jackson-jaxrs-json-provider-2.11.1.jar, jackson-module-jaxb-annotations-2.11.1.jar, jboss-jaxb-api_2.3_spec-1.0.1.Final.jar, json-patch-1.13.jar, microprofile-config-api-1.4.jar, msg-simple-1.2.jar define 1 overlapping resource: 
[WARNING]   - META-INF/LICENSE
[WARNING] jakarta.activation-1.2.1.jar, jakarta.activation-api-1.2.1.jar define 31 overlapping classes: 
[WARNING]   - javax.activation.ActivationDataFlavor
[WARNING]   - javax.activation.CommandInfo
[WARNING]   - javax.activation.CommandInfo$Beans
[WARNING]   - javax.activation.CommandInfo$Beans$1
[WARNING]   - javax.activation.CommandMap
[WARNING]   - javax.activation.CommandObject
[WARNING]   - javax.activation.DataContentHandler
[WARNING]   - javax.activation.DataContentHandlerFactory
[WARNING]   - javax.activation.DataHandler
[WARNING]   - javax.activation.DataHandler$1
[WARNING]   - 21 more...
[WARNING] resteasy-client-4.5.8.Final.jar, resteasy-context-propagation-4.5.8.Final.jar, resteasy-core-4.5.8.Final.jar, resteasy-jackson2-provider-4.5.8.Final.jar, resteasy-jaxb-provider-4.5.8.Final.jar, resteasy-reactor-4.5.8.Final.jar define 1 overlapping resource: 
[WARNING]   - META-INF/services/javax.ws.rs.ext.Providers
[WARNING] httpclient-4.5.12.jar, httpcore-4.4.13.jar, jackson-core-2.11.1.jar, jackson-databind-2.11.1.jar, jackson-jaxrs-json-provider-2.11.1.jar, jackson-module-jaxb-annotations-2.11.1.jar, microprofile-config-api-1.4.jar define 1 overlapping resource: 
[WARNING]   - META-INF/NOTICE
[WARNING] httpclient-4.5.12.jar, httpcore-4.4.13.jar define 1 overlapping resource: 
[WARNING]   - META-INF/DEPENDENCIES
[WARNING] commons-codec-1.13.jar, commons-io-2.5.jar, commons-logging-1.2.jar define 1 overlapping resource: 
[WARNING]   - META-INF/NOTICE.txt
[WARNING] btf-1.3.jar, jackson-coreutils-2.0.jar, json-patch-1.13.jar, msg-simple-1.2.jar define 2 overlapping resources: 
[WARNING]   - META-INF/ASL-2.0.txt
[WARNING]   - META-INF/LGPL-3.0.txt
[WARNING] netty-common-4.1.49.Final.jar, reactor-core-3.4.0.jar define 1 overlapping resource: 
[WARNING]   - META-INF/services/reactor.blockhound.integration.BlockHoundIntegration
[WARNING] netty-buffer-4.1.49.Final.jar, netty-codec-4.1.49.Final.jar, netty-codec-dns-4.1.49.Final.jar, netty-codec-http-4.1.49.Final.jar, netty-codec-http2-4.1.49.Final.jar, netty-codec-socks-4.1.49.Final.jar, netty-common-4.1.49.Final.jar, netty-handler-4.1.49.Final.jar, netty-handler-proxy-4.1.49.Final.jar, netty-resolver-4.1.49.Final.jar, netty-resolver-dns-4.1.49.Final.jar, netty-transport-4.1.49.Final.jar define 1 overlapping resource: 
[WARNING]   - META-INF/io.netty.versions.properties
[WARNING] maven-shade-plugin has detected that some class files are
[WARNING] present in two or more JARs. When this happens, only one
[WARNING] single version of the class is copied to the uber jar.
[WARNING] Usually this is not harmful and you can skip these warnings,
[WARNING] otherwise try to manually exclude artifacts based on
[WARNING] mvn dependency:tree -Ddetail=true and the above output.
[WARNING] See http://maven.apache.org/plugins/maven-shade-plugin/

で、今回は特にこれが問題になっていますね。

[WARNING] resteasy-client-4.5.8.Final.jar, resteasy-context-propagation-4.5.8.Final.jar, resteasy-core-4.5.8.Final.jar, resteasy-jackson2-provider-4.5.8.Final.jar, resteasy-jaxb-provider-4.5.8.Final.jar, resteasy-reactor-4.5.8.Final.jar define 1 overlapping resource: 
[WARNING]   - META-INF/services/javax.ws.rs.ext.Providers

マージ後はどうなっているんでしょう?

$ unzip -p target/maven-shade-plugin-example-0.1-shaded.jar META-INF/services/javax.ws.rs.ext.Providers
org.jboss.resteasy.client.jaxrs.internal.CompletionStageRxInvokerProvider

ひとつだけ残っています…。

ここで、ServicesResourceTransformerを使います。

Concatenating Service Entries with the ServicesResourceTransformer

                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <manifestEntries>
                                <Main-Class>org.littlewings.maven.shade.App</Main-Class>
                            </manifestEntries>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                    </transformers>
                </configuration>

JARファイルを再度作成して起動。

$ java -jar target/maven-shade-plugin-example-0.1-shaded.jar 
11月 22, 2020 11:37:36 午後 org.littlewings.maven.shade.App main
INFO: startup, server[8080]

確認。

$ curl -i -H 'Content-Type: application/json' localhost:8080/echo -d '{"message": "Hello World!!"}'
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/json

{"message":"reply: Hello World!!"}

今度はOKでした。

マージ後のファイルはどうなったんでしょう?

$ unzip -p target/maven-shade-plugin-example-0.1-shaded.jar META-INF/services/javax.ws.rs.ext.Providers
org.jboss.resteasy.client.jaxrs.internal.CompletionStageRxInvokerProvider
org.jboss.resteasy.plugins.providers.AsyncStreamingOutputProvider
org.jboss.resteasy.plugins.providers.DataSourceProvider
org.jboss.resteasy.plugins.providers.DocumentProvider
org.jboss.resteasy.plugins.providers.DefaultTextPlain
org.jboss.resteasy.plugins.providers.DefaultNumberWriter
org.jboss.resteasy.plugins.providers.DefaultBooleanWriter
org.jboss.resteasy.plugins.providers.StringTextStar
org.jboss.resteasy.plugins.providers.SourceProvider
org.jboss.resteasy.plugins.providers.InputStreamProvider
org.jboss.resteasy.plugins.providers.ReaderProvider
org.jboss.resteasy.plugins.providers.ByteArrayProvider
org.jboss.resteasy.plugins.providers.FormUrlEncodedProvider
org.jboss.resteasy.plugins.providers.JaxrsFormProvider
org.jboss.resteasy.plugins.providers.CompletionStageProvider
org.jboss.resteasy.plugins.providers.ReactiveStreamProvider
org.jboss.resteasy.plugins.providers.FileProvider
org.jboss.resteasy.plugins.providers.FileRangeWriter
org.jboss.resteasy.plugins.providers.StreamingOutputProvider
org.jboss.resteasy.plugins.providers.IIOImageProvider
org.jboss.resteasy.plugins.providers.MultiValuedParamConverterProvider
org.jboss.resteasy.plugins.interceptors.CacheControlFeature
org.jboss.resteasy.plugins.interceptors.ClientContentEncodingAnnotationFeature
org.jboss.resteasy.plugins.interceptors.ServerContentEncodingAnnotationFeature
org.jboss.resteasy.plugins.interceptors.MessageSanitizerContainerResponseFilter
org.jboss.resteasy.plugins.providers.sse.SseEventProvider
org.jboss.resteasy.plugins.providers.sse.SseEventSinkInterceptor

org.jboss.resteasy.reactor.MonoProvider
org.jboss.resteasy.reactor.MonoRxInvokerImpl
org.jboss.resteasy.reactor.MonoRxInvokerProvider
org.jboss.resteasy.reactor.FluxProvider
org.jboss.resteasy.reactor.FluxRxInvokerImpl
org.jboss.resteasy.reactor.FluxRxInvokerProvider
org.jboss.resteasy.context.ContextFeature
org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider
org.jboss.resteasy.plugins.providers.jackson.UnrecognizedPropertyExceptionHandler
org.jboss.resteasy.plugins.providers.jackson.PatchMethodFilter
org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlSeeAlsoProvider
org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlRootElementProvider
org.jboss.resteasy.plugins.providers.jaxb.JAXBElementProvider
org.jboss.resteasy.plugins.providers.jaxb.JAXBXmlTypeProvider
org.jboss.resteasy.plugins.providers.jaxb.CollectionProvider
org.jboss.resteasy.plugins.providers.jaxb.MapProvider
org.jboss.resteasy.plugins.providers.jaxb.XmlJAXBContextFinder

めちゃくちゃ増えました…。

PropertiesTransformerでプロパティファイルをマージする

WARNINGを見ていると、プロパティファイルも重複しているようです。

[WARNING] netty-buffer-4.1.49.Final.jar, netty-codec-4.1.49.Final.jar, netty-codec-dns-4.1.49.Final.jar, netty-codec-http-4.1.49.Final.jar, netty-codec-http2-4.1.49.Final.jar, netty-codec-socks-4.1.49.Final.jar, netty-common-4.1.49.Final.jar, netty-handler-4.1.49.Final.jar, netty-handler-proxy-4.1.49.Final.jar, netty-resolver-4.1.49.Final.jar, netty-resolver-dns-4.1.49.Final.jar, netty-transport-4.1.49.Final.jar define 1 overlapping resource: 
[WARNING]   - META-INF/io.netty.versions.properties

これを、PropertiesTransformerを使ってマージしてみましょう。

Merging properties files with PropertiesTransformer

現時点での中身を確認。

$ unzip -p target/maven-shade-plugin-example-0.1-shaded.jar META-INF/io.netty.versions.properties
#Generated by netty-parent/pom.xml
#Wed, 22 Apr 2020 10:53:19 +0000

netty-common.version=4.1.49.Final
netty-common.buildDate=2020-04-22 10\:53\:19 +0000
netty-common.commitDate=2020-04-22 09\:57\:26 +0000
netty-common.shortCommitHash=d0ec961
netty-common.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-common.repoStatus=clean

PropertiesTransformerを設定。

                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <manifestEntries>
                                <Main-Class>org.littlewings.maven.shade.App</Main-Class>
                            </manifestEntries>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer">
                            <resource>META-INF/io.netty.versions.properties</resource>
                            <alreadyMergedKey>already_merged</alreadyMergedKey>
                        </transformer>
                    </transformers>
                </configuration>

パッケージングして、プロパティファイルの中身を確認。

$ unzip -p target/maven-shade-plugin-example-0.1-shaded.jar META-INF/io.netty.versions.properties
# Merged by maven-shade-plugin (org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer)
netty-buffer.buildDate=2020-04-22 10\:54\:59 +0000
netty-buffer.commitDate=2020-04-22 09\:57\:26 +0000
netty-buffer.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-buffer.repoStatus=clean
netty-buffer.shortCommitHash=d0ec961
netty-buffer.version=4.1.49.Final
netty-codec-dns.buildDate=2020-04-22 10\:59\:08 +0000
netty-codec-dns.commitDate=2020-04-22 09\:57\:26 +0000
netty-codec-dns.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-codec-dns.repoStatus=clean
netty-codec-dns.shortCommitHash=d0ec961
netty-codec-dns.version=4.1.49.Final
netty-codec-http.buildDate=2020-04-22 11\:04\:06 +0000
netty-codec-http.commitDate=2020-04-22 09\:57\:26 +0000
netty-codec-http.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-codec-http.repoStatus=clean
netty-codec-http.shortCommitHash=d0ec961
netty-codec-http.version=4.1.49.Final
netty-codec-http2.buildDate=2020-04-22 11\:04\:52 +0000
netty-codec-http2.commitDate=2020-04-22 09\:57\:26 +0000
netty-codec-http2.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-codec-http2.repoStatus=clean
netty-codec-http2.shortCommitHash=d0ec961
netty-codec-http2.version=4.1.49.Final
netty-codec-socks.buildDate=2020-04-22 11\:06\:56 +0000
netty-codec-socks.commitDate=2020-04-22 09\:57\:26 +0000
netty-codec-socks.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-codec-socks.repoStatus=clean
netty-codec-socks.shortCommitHash=d0ec961
netty-codec-socks.version=4.1.49.Final
netty-codec.buildDate=2020-04-22 10\:58\:33 +0000
netty-codec.commitDate=2020-04-22 09\:57\:26 +0000
netty-codec.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-codec.repoStatus=clean
netty-codec.shortCommitHash=d0ec961
netty-codec.version=4.1.49.Final
netty-common.buildDate=2020-04-22 10\:53\:19 +0000
netty-common.commitDate=2020-04-22 09\:57\:26 +0000
netty-common.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-common.repoStatus=clean
netty-common.shortCommitHash=d0ec961
netty-common.version=4.1.49.Final
netty-handler-proxy.buildDate=2020-04-22 11\:07\:55 +0000
netty-handler-proxy.commitDate=2020-04-22 09\:57\:26 +0000
netty-handler-proxy.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-handler-proxy.repoStatus=clean
netty-handler-proxy.shortCommitHash=d0ec961
netty-handler-proxy.version=4.1.49.Final
netty-handler.buildDate=2020-04-22 11\:00\:05 +0000
netty-handler.commitDate=2020-04-22 09\:57\:26 +0000
netty-handler.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-handler.repoStatus=clean
netty-handler.shortCommitHash=d0ec961
netty-handler.version=4.1.49.Final
netty-resolver-dns.buildDate=2020-04-22 11\:08\:26 +0000
netty-resolver-dns.commitDate=2020-04-22 09\:57\:26 +0000
netty-resolver-dns.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-resolver-dns.repoStatus=clean
netty-resolver-dns.shortCommitHash=d0ec961
netty-resolver-dns.version=4.1.49.Final
netty-resolver.buildDate=2020-04-22 10\:56\:46 +0000
netty-resolver.commitDate=2020-04-22 09\:57\:26 +0000
netty-resolver.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-resolver.repoStatus=clean
netty-resolver.shortCommitHash=d0ec961
netty-resolver.version=4.1.49.Final
netty-transport.buildDate=2020-04-22 10\:57\:08 +0000
netty-transport.commitDate=2020-04-22 09\:57\:26 +0000
netty-transport.longCommitHash=d0ec961cce19646519d6a0d59e7604b0eacd9bf2
netty-transport.repoStatus=clean
netty-transport.shortCommitHash=d0ec961
netty-transport.version=4.1.49.Final

めちゃくちゃ増えました…。

ライセンスの破棄やNOTICEのマージ

最後に、ライブラリに含まれているLICENSEおよびLICENSE.txtを破棄して

[WARNING] cdi-api-2.0.SP1.jar, commons-codec-1.13.jar, commons-io-2.5.jar, commons-logging-1.2.jar, javax.el-api-3.0.0.jar, javax.interceptor-api-1.2.jar, jboss-jaxb-api_2.3_spec-1.0.1.Final.jar, jboss-logging-3.3.2.Final.jar define 1 overlapping resource: 
[WARNING]   - META-INF/LICENSE.txt
[WARNING] btf-1.3.jar, httpclient-4.5.12.jar, httpcore-4.4.13.jar, jackson-annotations-2.11.1.jar, jackson-core-2.11.1.jar, jackson-coreutils-2.0.jar, jackson-databind-2.11.1.jar, jackson-jaxrs-base-2.11.1.jar, jackson-jaxrs-json-provider-2.11.1.jar, jackson-module-jaxb-annotations-2.11.1.jar, jboss-jaxb-api_2.3_spec-1.0.1.Final.jar, json-patch-1.13.jar, microprofile-config-api-1.4.jar, msg-simple-1.2.jar define 1 overlapping resource: 
[WARNING]   - META-INF/LICENSE

Preventing License Duplication with the ApacheLicenseResourceTransformer

NOTICEファイルをマージします。

[WARNING] httpclient-4.5.12.jar, httpcore-4.4.13.jar, jackson-core-2.11.1.jar, jackson-databind-2.11.1.jar, jackson-jaxrs-json-provider-2.11.1.jar, jackson-module-jaxb-annotations-2.11.1.jar, microprofile-config-api-1.4.jar define 1 overlapping resource: 
[WARNING]   - META-INF/NOTICE
[WARNING] commons-codec-1.13.jar, commons-io-2.5.jar, commons-logging-1.2.jar define 1 overlapping resource: 
[WARNING]   - META-INF/NOTICE.txt

Aggregating Notices with the ApacheNoticeResourceTransformer

設定。

                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <manifestEntries>
                                <Main-Class>org.littlewings.maven.shade.App</Main-Class>
                            </manifestEntries>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer">
                            <resource>META-INF/io.netty.versions.properties</resource>
                            <alreadyMergedKey>already_merged</alreadyMergedKey>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer">
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
                            <addHeader>false</addHeader>
                        </transformer>
                    </transformers>
                </configuration>

これで、LICENSE.txtおよびLICENSEファイルがなくなります。

$ unzip -l target/maven-shade-plugin-example-0.1-shaded.jar | grep LICENSE
    35084  2019-06-16 21:23   META-INF/LICENSE.md

.mdの方が残っていますが…。

NOTICEファイルは、マージされます。

$ unzip -p target/maven-shade-plugin-example-0.1-shaded.jar META-INF/NOTICE

Apache Commons IO
Copyright 2002-2016 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

=========================================================================
==  NOTICE file corresponding to section 4(d) of the Apache License,   ==
==  Version 2.0, in this case for Microprofile Config                  ==
=========================================================================

Portions of this software were originally based on the following:
* Apache DeltaSpike Config
  https://deltaspike.apache.org
  under Apache License, v2.0

SPDXVersion: SPDX-2.1
PackageName: Eclipse Microprofile
PackageHomePage: http://www.eclipse.org/microprofile
PackageLicenseDeclared: Apache-2.0

PackageCopyrightText: <text>
Mark Struberg struberg@apache.org,
Gerhard Petracek gpetracek@apache.org,
Romain Manni-Bucau rmannibucau@apache.org,
Ron Smeral rsmeral@apache.org,
Emily Jiang emijiang@uk.ibm.com,
Ondrej Mihalyi ondrej.mihalyi@gmail.com,
Gunnar Morling gunnar@hibernate.org
</text>

Apache HttpClient
Copyright 1999-2020 The Apache Software Foundation

Apache HttpCore
Copyright 2005-2020 The Apache Software Foundation

Apache Commons Logging
Copyright 2003-2014 The Apache Software Foundation

Apache Commons Codec
Copyright 2002-2019 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).

src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
contains test data from http://aspell.net/test/orig/batch0.tab.
Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)

===============================================================================

The content of package org.apache.commons.codec.language.bm has been translated
from the original php source code available at http://stevemorse.org/phoneticinfo.htm
with permission from the original authors.
Original source copyright:
Copyright (c) 2008 Alexander Beider & Stephen P. Morse.

# Jackson JSON processor

Jackson is a high-performance, Free/Open Source JSON processing library.
It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
been in development since 2007.
It is currently developed by a community of developers, as well as supported
commercially by FasterXML.com.

## Licensing

Jackson core and extension components may licensed under different licenses.
To find the details that apply to this artifact see the accompanying LICENSE file.
For more information, including possible other licensing options, contact
FasterXML.com (http://fasterxml.com).

## Credits

A list of contributors may be found from CREDITS file, which is included
in some artifacts (usually source distributions); but is always available
from the source code management (SCM) system project uses.

Jackson core and extension components may be licensed under different licenses.
To find the details that apply to this artifact see the accompanying LICENSE file.
For more information, including possible other licensing options, contact
FasterXML.com (http://fasterxml.com).

今回は、ざっとこんな感じで。

まとめ

これまでMaven Shade Pluginをそれほど使っていなかったというのと、使っても雰囲気でコピー&ペーストしていてあまり
ちゃんと見ていなかったので、これを機に見てみてだいぶ雰囲気わかった気がします。

今後は、もうちょっとちゃんと使いましょう…。

最後に、今回のMaven Shade Pluginの設定全体を載せておきます。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <shadedArtifactAttached>true</shadedArtifactAttached>
                    <transformers>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                            <manifestEntries>
                                <Main-Class>org.littlewings.maven.shade.App</Main-Class>
                            </manifestEntries>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer">
                            <resource>META-INF/io.netty.versions.properties</resource>
                            <alreadyMergedKey>already_merged</alreadyMergedKey>
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer">
                        </transformer>
                        <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
                            <addHeader>false</addHeader>
                        </transformer>
                    </transformers>
                </configuration>
            </plugin>