CLOVER🍀

That was when it all began.

Vert.x API Generationをちょっと調べつつ、Vert.xのRedis ClientをSmallRye Mutinyに対応させてみる

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

Quarkusには、SmallRye Mutinyに対応したReactiveなSQLクライアントやRedisクライアントなどがあります。

Quarkus - Reactive SQL Clients

Quarkus - Using the Redis Client

これらは、Vert.xのライブラリを元にしているものですが、Vert.x自体はSmallRye Mutinyには対応していません。

このあたりのからくりを、時々調べては忘れてしまうのでメモしておこうかな、と。

あと、ここで紹介するVert.x API Genratorを使って、Redisを題材にVert.xのRedisクライアントをSmallRye Mutinyに対応させて
みましょう。

からくり?

QuarkusのRedis Extensionは、smallrye-mutiny-vertx-redis-clientというライブラリに依存しています。

https://github.com/quarkusio/quarkus/blob/1.9.2.Final/extensions/redis-client/runtime/pom.xml#L23-L26

https://github.com/quarkusio/quarkus/blob/1.9.2.Final/bom/application/pom.xml#L135

smallrye-mutiny-vertx-redis-clientというライブラリは、こちらのリポジトリに含まれています。

GitHub - smallrye/smallrye-reactive-utils

で、リポジトリを見ると気づくのですが、このライブラリにはsrc/main/javaがありません。テストコードは
あるのですが。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client

そもそも、Vert.xのRedisクライアントのドキュメントを見てもReactive StreamsなAPIが使える雰囲気は感じません。

Vert.x-redis - Vert.x

さて、これはどういうことでしょう?というのが、今回のお題です。

Vert.x API Generation

smallrye-mutiny-vertx-redis-clientpom.xmlを見ていると、どうもソースコードを自動生成していそうな雰囲気があります。

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client/pom.xml

特にこのあたりですね。

            <plugin>
                <groupId>org.bsc.maven</groupId>
                <artifactId>maven-processor-plugin</artifactId>
                <version>4.2-jdk8</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.SimpleFormatter.format>%4$s: %3$s - %5$s %6$s%n
                        </java.util.logging.SimpleFormatter.format>
                        <mvel2.disable.jit>true</mvel2.disable.jit>
                    </systemProperties>
                </configuration>
                <executions>
                    <!-- Run the annotation processor on java sources and generate the API -->
                    <execution>
                        <id>generate-api</id>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceDirectory>${project.build.directory}/sources/java</sourceDirectory>
                            <processors>
                                <processor>io.vertx.codegen.CodeGenProcessor</processor>
                            </processors>
                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

ここで動いているのが、Vert.x API Generationです。

GitHub - vert-x3/vertx-codegen: Vert.x code generator for asynchronous polyglot APIs

あまり詳細を追うつもりはないですが(?)、io.vertx.codegen.Generatorの実装クラスをロードする
io.vertx.codegen.GeneratorLoaderの実装をMETA-INF/services/io.vertx.codegen.GeneratorLoaderで指定する、
要はService Loaderの仕組みを使って以下のようなことができるそうです。

  • Data object converters
  • Service proxy and service handler
  • RxJava-ified classes API
  • Groovy extension methods API

今回は、SmallRye MutinyスタイルのAPIを生成するのに使います、と。

SmalleRye Mutiny向けに用意されているのは、こちらですね。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/vertx-mutiny-clients/vertx-mutiny-generator

GeneratorLoaderの指定。

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-generator/src/main/resources/META-INF/services/io.vertx.codegen.GeneratorLoader

先ほど記載したpom.xmlでこんな記述がありましたが

                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>

この指定で使われるGeneratorはこちらになります。

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-generator/src/main/java/io/smallrye/mutiny/vertx/codegen/MutinyGenerator.java

というわけで、smallrye-mutiny-vertx-redis-clientpom.xmlをもう少し読み解いてみると。

vertx-redis-clientをターゲットにして

    <properties>
        <gen-source-groupId>io.vertx</gen-source-groupId>
        <gen-source-artifactId>vertx-redis-client</gen-source-artifactId>
        <gen.output>${project.build.directory}/sources/java</gen.output>
    </properties>

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client/pom.xml#L14-L18

Maven Dependency Pluginでvertx-redis-clientソースコードをダウンロードして展開、

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <includeGroupIds>${gen-source-groupId}</includeGroupIds>
                    <includeArtifactIds>${gen-source-artifactId}</includeArtifactIds>
                    <classifier>sources</classifier>
                    <includeTypes>jar</includeTypes>
                </configuration>
                <executions>
                    <!-- Unpack java sources to target/java-sources -->
                    <execution>
                        <id>unpack-java</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includes>io/vertx/**/*.java</includes>
                            <excludes>**/impl/**/*.java</excludes>
                            <outputDirectory>${gen.output}</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client/pom.xml#L53-L76

展開されたソースコードをインプットに、SmallRye Mutinyに対応したソースコードを生成してパッケージングしている、
というのが実体のようです。

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client/pom.xml#L78-L108

            <plugin>
                <groupId>org.bsc.maven</groupId>
                <artifactId>maven-processor-plugin</artifactId>
                <version>4.2-jdk8</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.SimpleFormatter.format>%4$s: %3$s - %5$s %6$s%n
                        </java.util.logging.SimpleFormatter.format>
                        <mvel2.disable.jit>true</mvel2.disable.jit>
                    </systemProperties>
                </configuration>
                <executions>
                    <!-- Run the annotation processor on java sources and generate the API -->
                    <execution>
                        <id>generate-api</id>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceDirectory>${project.build.directory}/sources/java</sourceDirectory>
                            <processors>
                                <processor>io.vertx.codegen.CodeGenProcessor</processor>
                            </processors>
                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

なるほど、と。

Quarkusは、この生成されたソースコードをさらにラップして提供している形態になります。

https://github.com/quarkusio/quarkus/blob/1.9.2.Final/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/ReactiveRedisClientImpl.java

というか、このリポジトリに含まれるクライアントライブラリ全体的に、こんな感じでソースコードの自動生成を元に
構成されている様子ですね。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/vertx-mutiny-clients

その他、このリポジトリにはReactive Convertersという変換ライブラリもあるようなので、こちらも覚えておいてよいかもしれません。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/reactive-converters

RxJava 1/2はどうした?

ところで、Vert.xはRxJava 1および2に対応していそうなドキュメントがあります。

Reactive

リポジトリはこちらです。

GitHub - vert-x3/vertx-rx: Reactive Extensions for Vert.x

中身を見てみると、こちらもVert.x API Generationを使ってVert.xのAPIから自動生成したものでした。

同じ仕組みですね!

API Constraint

こんな感じで書いていくと、あるソースコードから特定のReactive Streamsの実装に対応したソースコードを出力してくれそうな
すごいもののような印象を受けますが、それなりに制限はあります。

API constraints

そもそもがPluggable Annotation Processing APIで実現されている仕組みなのでアノテーションの付与が必要ですし、
アノテーションが付与される対象はインターフェースであることとかいろいろあります。

たとえば、以下のインターフェースがVert.x API Generationに関するアノテーションが付与されたクラスですね。

https://github.com/vert-x3/vertx-redis-client/blob/3.9.4/src/main/java/io/vertx/redis/client/RedisAPI.java

つまり、このインターフェースはソースコード生成対象となります。

というわけで

とまあ、ここまでで理屈はなんとなくわかりました。

ここで終わってもいいのですが、1度自分でコード生成を行ってみてもよいのではないでしょうか。

というわけで、smallrye-mutiny-vertx-redis-clientを参考に、自分でvertx-redis-clientをSmallRye Mutinyに対応させてみましょう。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client

コード生成およびJARの作成と、それを使ったテストコードによる簡単な動作確認をゴールにします。

これらは、Mavenのマルチプロジェクトで構成するとしましょう。

環境

今回の環境は、こちらです。

$ 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"

Vert.xは3.9.4を使用します。

親プロジェクト

親プロジェクトを用意します。含まれるサブモジュールが定義されているだけですが。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>vertx-codegen-redis-example</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <modules>
        <module>hello-redis-client</module>
        <module>my-smallrye-mutiny-vertx-redis-client</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
</project>

my-smallrye-mutiny-vertx-redis-clientが自前で作る、SmallRye Mutinyに対応したRedisクライアント、
hello-redis-clientmy-smallrye-mutiny-vertx-redis-clientを使ったテストコードを書く形にします。

    <modules>
        <module>hello-redis-client</module>
        <module>my-smallrye-mutiny-vertx-redis-client</module>
    </modules>

vertx-redis-clientをSmallRye Mutinyに対応させる

では、vertx-redis-clientをSmallRye Mutinyに対応させるプロジェクトを作ります。自前のsmallrye-mutiny-vertx-redis-clientです。

といっても、用意するのはpom.xmlだけでいいのですが。

Vert.x API GenerationのREADME.mdを見ると、Maven Compiler Pluginでの設定が行われているのですが

Processor configuration

オリジナルのsmallrye-mutiny-vertx-redis-clientを見ると、Maven Processor Pluginで設定が行われています。

https://github.com/smallrye/smallrye-reactive-utils/blob/1.2.2/vertx-mutiny-clients/vertx-mutiny-redis-client/pom.xml#L78-L108

こちらのプラグインですね。

MAVEN PROCESSOR PLUGIN – About

RxJava向けのソースコードを生成しているプロジェクトを見ても、同じでした。

GitHub - vert-x3/vertx-rx: Reactive Extensions for Vert.x

どうしようかと迷ったところで、両方やりましょう

やることは、次の内容です。

  • vertx-redis-clientをベースに、SmallRye Mutinyに対応するライブラリを作る
  • vertx-redis-clientソースコードMaven Dependency Pluginを使い、ダウンロードする
    • ダウンロードしたソースコードの展開先は${project.build.directory}/sources/javaとする(オリジナルのsmallrye-mutiny-vertx-redis-clientと同じ)
    • ここに配置されたソースコードをVert.x API GenerationのPluggable Annotation Processing APIで処理する
  • Vert.x API Generationで処理する際には、SmallRye Mutiny向けのGeneratorを指定する

どちらのMavenプラグインを使った方法でも共通する部分は、最初に貼っておきます(最後に全体は載せます)。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>vertx-codegen-redis-example</artifactId>
        <groupId>org.littlewings</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>my-smallrye-mutiny-vertx-redis-client</artifactId>

〜省略〜

</project>
Maven Compiler Pluginでソースコードを生成する

最初は、Vert.x API GenerationのREADME.mdに沿って、Maven Compiler Pluginで設定を行います。

ライブラリ依存関係はオリジナルのsmallrye-mutiny-vertx-redis-clientに習いますが、こちらのvertx-codegenの明示的な追加が
必要です。

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-redis-client</artifactId>
            <version>3.9.4</version>
        </dependency>

        <dependency>
            <groupId>io.smallrye.reactive</groupId>
            <artifactId>smallrye-mutiny-vertx-core</artifactId>
            <version>1.2.2</version>
        </dependency>

        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-codegen</artifactId>
            <classifier>processor</classifier>
            <version>3.9.4</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

vertx-codegenが必要なのはドキュメント通りなのですが、Maven Processor Pluginを使った時は依存関係に加えなくても
動作するんですよねぇ…。

Processor configuration

ちなみに、vertx-codegenへの依存関係はsmallrye-mutiny-vertx-coreに含まれているのですが、processor classifierのものを
依存関係に加える必要があります。

processor classifier有無の差は、どうやらMETA-INF/services/javax.annotation.processing.Processorが含まれているか
どうかのようで、これがないとMaven Compiler Pluginの場合はPluggable Annotation Processing APIが起動しない…というか、
それがふつうですね…。

Maven Processor Pluginが不思議なのか…。

あとはMavenプラグインの設定です。こんな感じにしました。

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <includeGroupIds>io.vertx</includeGroupIds>
                    <includeArtifactIds>vertx-redis-client</includeArtifactIds>
                    <classifier>sources</classifier>
                    <includeTypes>jar</includeTypes>
                </configuration>
                <executions>
                    <execution>
                        <id>unpack-java</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includes>io/vertx/**/*.java</includes>
                            <excludes>**/impl/**/*.java</excludes>
                            <outputDirectory>${project.build.directory}/sources/java</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/sources/java</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <useIncrementalCompilation>false</useIncrementalCompilation>
                </configuration>
                <executions>
                    <execution>
                        <id>generate-mutiny-source</id>
                        <configuration>
                            <annotationProcessors>
                                <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
                            </annotationProcessors>
                            <compilerArgs>
                                <arg>-Acodegen.generators=mutiny</arg>
                            </compilerArgs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Maven Dependency Pluginでvertx-redis-clientソースコードを取得して、${project.build.directory}/sources/javaに展開。
includesexcludesの指定は、オリジナルのsmallrye-mutiny-vertx-redis-clientの設定をそのまま使っています。

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <includeGroupIds>io.vertx</includeGroupIds>
                    <includeArtifactIds>vertx-redis-client</includeArtifactIds>
                    <classifier>sources</classifier>
                    <includeTypes>jar</includeTypes>
                </configuration>
                <executions>
                    <execution>
                        <id>unpack-java</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includes>io/vertx/**/*.java</includes>
                            <excludes>**/impl/**/*.java</excludes>
                            <outputDirectory>${project.build.directory}/sources/java</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

展開したvertx-redis-clientソースコードを、ビルド対象に追加。

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/sources/java</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

最後に、Vert.x API Generationを使ってソースコードを生成します。

            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <useIncrementalCompilation>false</useIncrementalCompilation>
                </configuration>
                <executions>
                    <execution>
                        <id>generate-mutiny-source</id>
                        <configuration>
                            <annotationProcessors>
                                <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
                            </annotationProcessors>
                            <compilerArgs>
                                <arg>-Acodegen.generators=mutiny</arg>
                            </compilerArgs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

アノテーションプロセッサーおよびSmallRye Mutiny向けのGeneratorの指定は、ここですね。

                            <annotationProcessors>
                                <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
                            </annotationProcessors>
                            <compilerArgs>
                                <arg>-Acodegen.generators=mutiny</arg>
                            </compilerArgs>

ビルドしてみましょう。

$ mvn package

できあがったJARファイルには、io.vertx.mutinyパッケージに属するクラスが含まれるようになっています。

$ jar -tvf target/my-smallrye-mutiny-vertx-redis-client-0.0.1-SNAPSHOT.jar 
     0 Mon Nov 23 23:01:18 JST 2020 META-INF/
   133 Mon Nov 23 23:01:16 JST 2020 META-INF/MANIFEST.MF
     0 Mon Nov 23 23:01:16 JST 2020 io/
     0 Mon Nov 23 23:01:16 JST 2020 io/vertx/
     0 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/
     0 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/
     0 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/
     0 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/sentinel/
182280 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/RedisTransaction.class
  3083 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/Script.class
196928 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/RedisClient.class
  1923 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/RedisTransaction$1.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$90.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$83.class
  1963 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$78.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$58.class
  1909 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$2.class
  1967 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$45.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$54.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$47.class
  1911 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$19.class
  1916 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$108.class
  1949 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$153.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$75.class
  1915 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$98.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$200.class
  1945 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$49.class
  1993 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/RedisConnection$2.class
  1936 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$116.class
  1369 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/RedisConnection$1.class
  1969 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$134.class
  1966 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$132.class
  1931 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$148.class
  1967 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$101.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$115.class
  1912 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$85.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$179.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$97.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$27.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$170.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$31.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$178.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$11.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$28.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$143.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$169.class
  1915 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$4.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$173.class
  1952 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$119.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$3.class
  1935 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$107.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$55.class
  1970 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$183.class
  1914 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$161.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$120.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$74.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$34.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$162.class
  1969 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$12.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$121.class
  1977 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$193.class
  1967 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$180.class
  1932 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$149.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$99.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$72.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$43.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$46.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$56.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$21.class
  1948 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$61.class
  1984 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$71.class
  5154 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/Request.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$184.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$6.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$167.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$100.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$87.class
  1923 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/Redis$2.class
  1931 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$135.class
  2983 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/RedisConnection$3.class
  1948 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$133.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$33.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$147.class
  1949 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$32.class
 15791 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/RedisConnection.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$95.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$76.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$165.class
  1933 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$128.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$177.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$81.class
  1951 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$197.class
129895 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$104.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$22.class
  1948 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$92.class
  1924 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$8.class
  1950 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$105.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$59.class
  1967 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$150.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$157.class
  1912 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$24.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$163.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$30.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$152.class
  1938 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$41.class
  1933 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$141.class
  9056 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/Redis.class
  1931 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$171.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$10.class
  1966 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$142.class
  1910 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$89.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$9.class
  1949 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$176.class
  1931 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$113.class
  1968 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$181.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$36.class
  1932 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$175.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$70.class
  1949 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$111.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$67.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$137.class
  1933 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$174.class
 14133 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/Command.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$5.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$96.class
  1976 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$192.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$64.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$136.class
  1966 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$51.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$156.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$145.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$50.class
  1951 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$112.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$151.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$185.class
  1950 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$93.class
  1964 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$80.class
  1945 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$1.class
  1937 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$195.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$39.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$62.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$73.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$158.class
  1939 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$196.class
  1967 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$131.class
  1909 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$29.class
  1965 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$60.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$66.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$146.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$26.class
  1913 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$68.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$125.class
  1932 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$194.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$186.class
  1936 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$188.class
  1948 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$189.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$94.class
  5522 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/client/Response.class
  1971 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$52.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$106.class
  1952 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$138.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$18.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$164.class
  1949 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$199.class
  1948 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$103.class
  1916 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$110.class
  1911 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$154.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$16.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$122.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$198.class
  1940 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$42.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$37.class
  1931 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$129.class
  1915 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$109.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$15.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$160.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$69.class
  1938 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/Redis$1.class
  1963 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$79.class
  1947 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$44.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$190.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$144.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$7.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$14.class
  1975 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$191.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$172.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$124.class
  1932 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$40.class
  1911 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$123.class
  1911 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$117.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$127.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$130.class
  1945 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$86.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$118.class
  1911 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$155.class
  1928 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$53.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$88.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$82.class
  2913 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/Redis$3.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$166.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$13.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$38.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$126.class
  1933 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$102.class
  1965 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$77.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$182.class
  1926 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$20.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$35.class
  1952 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$65.class
  1912 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$17.class
  1934 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$187.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$25.class
  1925 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$57.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$168.class
  1948 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$48.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$140.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$84.class
  1952 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$114.class
  1929 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$159.class
  1930 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$91.class
  1924 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$23.class
  1950 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$139.class
  1927 Mon Nov 23 23:01:18 JST 2020 io/vertx/mutiny/redis/client/RedisAPI$63.class
 10817 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/sentinel/RedisSentinel.class
  1888 Mon Nov 23 23:01:16 JST 2020 io/vertx/mutiny/redis/RedisClient$1.class
     0 Mon Nov 23 23:01:18 JST 2020 META-INF/maven/
     0 Mon Nov 23 23:01:18 JST 2020 META-INF/maven/org.littlewings/
     0 Mon Nov 23 23:01:18 JST 2020 META-INF/maven/org.littlewings/my-smallrye-mutiny-vertx-redis-client/
  3479 Mon Nov 23 22:21:48 JST 2020 META-INF/maven/org.littlewings/my-smallrye-mutiny-vertx-redis-client/pom.xml
   146 Mon Nov 23 23:01:18 JST 2020 META-INF/maven/org.littlewings/my-smallrye-mutiny-vertx-redis-client/pom.properties

生成されたソースコードは、こちら。

$ find target/generated-sources -type f
target/generated-sources/apt/io/vertx/mutiny/redis/Script.java
target/generated-sources/apt/io/vertx/mutiny/redis/RedisTransaction.java
target/generated-sources/apt/io/vertx/mutiny/redis/RedisClient.java
target/generated-sources/apt/io/vertx/mutiny/redis/client/Request.java
target/generated-sources/apt/io/vertx/mutiny/redis/client/Command.java
target/generated-sources/apt/io/vertx/mutiny/redis/client/RedisConnection.java
target/generated-sources/apt/io/vertx/mutiny/redis/client/Redis.java
target/generated-sources/apt/io/vertx/mutiny/redis/client/Response.java
target/generated-sources/apt/io/vertx/mutiny/redis/client/RedisAPI.java
target/generated-sources/apt/io/vertx/mutiny/redis/sentinel/RedisSentinel.java

生成されたソースコードの雰囲気は、こんな感じです。
target/generated-sources/annotations/io/vertx/mutiny/redis/client/RedisAPI.java

package io.vertx.mutiny.redis.client;

import java.util.Map;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import java.util.function.Consumer;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Publisher;
import java.util.List;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;

/**
 * <b>Auto generated</b> Redis API client wrapper.
 *
 * <p/>
 * NOTE: This class has been automatically generated from the {@link io.vertx.redis.client.RedisAPI original} non Mutiny-ified interface using Vert.x codegen.
 */

@io.smallrye.mutiny.vertx.MutinyGen(io.vertx.redis.client.RedisAPI.class)
public class RedisAPI {

  public static final io.smallrye.mutiny.vertx.TypeArg<RedisAPI> __TYPE_ARG = new io.smallrye.mutiny.vertx.TypeArg<>(    obj -> new RedisAPI((io.vertx.redis.client.RedisAPI) obj),
    RedisAPI::getDelegate
  );

  private final io.vertx.redis.client.RedisAPI delegate;
  
  public RedisAPI(io.vertx.redis.client.RedisAPI delegate) {
    this.delegate = delegate;
  }

  /**
  * Empty constructor used by CDI, do not use this constructor directly.
  **/
  RedisAPI() {    this.delegate = null;
  }

  public io.vertx.redis.client.RedisAPI getDelegate() {
    return delegate;
  }

  @Override
  public String toString() {
    return delegate.toString();
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    RedisAPI that = (RedisAPI) o;
    return delegate.equals(that.delegate);
  }
  
  @Override
  public int hashCode() {
    return delegate.hashCode();
  }

  public void close() { 
    delegate.close();
  }

  /**
   * @param arg0 
   * @param arg1 
   * @param handler 
   * @return fluent self
   */
  private io.vertx.mutiny.redis.client.RedisAPI __append(String arg0, String arg1, Handler<AsyncResult<io.vertx.mutiny.redis.client.Response>> handler) { 
    delegate.append(arg0, arg1, new Handler<AsyncResult<io.vertx.redis.client.Response>>() {
      public void handle(AsyncResult<io.vertx.redis.client.Response> ar) {
        if (ar.succeeded()) {
          handler.handle(io.vertx.core.Future.succeededFuture(io.vertx.mutiny.redis.client.Response.newInstance(ar.result())));
        } else {
          handler.handle(io.vertx.core.Future.failedFuture(ar.cause()));
        }
      }
    });
    return this;
  }

  /**
   * Redis command <a href="https://redis.io/commands/append">append</a>.
   * <p>
   * Unlike the <em>bare</em> Vert.x variant, this method returns a {@link io.smallrye.mutiny.Uni Uni}.
   * Don't forget to <em>subscribe</em> on it to trigger the operation.
   * @param arg0 
   * @param arg1 
   * @return fluent self
   */
  public Uni<io.vertx.mutiny.redis.client.Response> append(String arg0, String arg1) { 
    return io.smallrye.mutiny.vertx.AsyncResultUni.toUni(handler -> {
      __append(arg0, arg1, handler);
    });
  }

〜省略〜
}

生成元のRedisAPIインターフェースは、こんな感じでした。

〜省略〜

@VertxGen
public interface RedisAPI {

  @GenIgnore(PERMITTED_TYPE)
  static RedisAPI api(Redis client) {
    return new RedisAPIImpl(client);
  }

  @GenIgnore(PERMITTED_TYPE)
  static RedisAPI api(RedisConnection connection) {
    return new RedisAPIImpl(connection);
  }

  void close();

  /**
   * Redis command <a href="https://redis.io/commands/append">append</a>.
   * @return fluent self
   */
  @Fluent
  default RedisAPI append(String arg0, String arg1, Handler<AsyncResult<@Nullable Response>> handler) {
    send(Command.APPEND, arg0, arg1).setHandler(handler);
    return this;
  }

  /**
   * Redis command <a href="https://redis.io/commands/asking">asking</a>.
   * @return fluent self
   */
  @Fluent
  default RedisAPI asking(Handler<AsyncResult<@Nullable Response>> handler) {
    send(Command.ASKING).setHandler(handler);
    return this;
  }

〜省略〜
}

https://github.com/vert-x3/vertx-redis-client/blob/3.9.4/src/main/java/io/vertx/redis/client/RedisAPI.java

とりあえず、こちらはここまで。

Maven Processor Pluginでソースコードを生成する

続いては、Maven Processor Pluginを使います。

ライブラリの依存関係は、これだけでOKです。

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-redis-client</artifactId>
            <version>3.9.4</version>
        </dependency>

        <dependency>
            <groupId>io.smallrye.reactive</groupId>
            <artifactId>smallrye-mutiny-vertx-core</artifactId>
            <version>1.2.2</version>
        </dependency>
    </dependencies>

Mavenプラグインの設定は、こんな感じに。

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <includeGroupIds>io.vertx</includeGroupIds>
                    <includeArtifactIds>vertx-redis-client</includeArtifactIds>
                    <classifier>sources</classifier>
                    <includeTypes>jar</includeTypes>
                </configuration>
                <executions>
                    <execution>
                        <id>unpack-java</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includes>io/vertx/**/*.java</includes>
                            <excludes>**/impl/**/*.java</excludes>
                            <outputDirectory>${project.build.directory}/sources/java</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.bsc.maven</groupId>
                <artifactId>maven-processor-plugin</artifactId>
                <version>4.4-jdk8</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.SimpleFormatter.format>%4$s: %3$s - %5$s %6$s%n</java.util.logging.SimpleFormatter.format>
                        <mvel2.disable.jit>true</mvel2.disable.jit>
                    </systemProperties>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceDirectory>${project.build.directory}/sources/java</sourceDirectory>
                            <processors>
                                <processor>io.vertx.codegen.CodeGenProcessor</processor>
                            </processors>
                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Maven Dependency Pluginの設定は、先ほどとまったく同じなので割愛します。

違うのはこちら。プロジェクトのソースディレクトリとしてvertx-redis-clientの展開先をsourceDirectoryで指定しています。

            <plugin>
                <groupId>org.bsc.maven</groupId>
                <artifactId>maven-processor-plugin</artifactId>
                <version>4.4-jdk8</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.SimpleFormatter.format>%4$s: %3$s - %5$s %6$s%n</java.util.logging.SimpleFormatter.format>
                        <mvel2.disable.jit>true</mvel2.disable.jit>
                    </systemProperties>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceDirectory>${project.build.directory}/sources/java</sourceDirectory>
                            <processors>
                                <processor>io.vertx.codegen.CodeGenProcessor</processor>
                            </processors>
                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

アノテーションプロセッサーの指定およびSmalleRye Mutiny向けのGeneratorの指定は、こちら。

                            <processors>
                                <processor>io.vertx.codegen.CodeGenProcessor</processor>
                            </processors>
                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>

パッケージング。

$ mvn package

結果はほぼ同じなので、割愛します。

生成したライブラリを使う

では、生成したライブラリを使ってみましょう。ライブラリ依存関係とMavenプラグインの設定は、こちら。

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>my-smallrye-mutiny-vertx-redis-client</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.18.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

テストコードで確認することにします。

自前のライブラリの部分は、こちらですね。

        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>my-smallrye-mutiny-vertx-redis-client</artifactId>
            <version>${project.version}</version>
        </dependency>

テストコード。
src/test/java/org/littlewings/vertx/codegen/mutiny/ReactiveRedisTest.java

package org.littlewings.vertx.codegen.mutiny;

import java.util.List;

import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.redis.client.Redis;
import io.vertx.mutiny.redis.client.RedisAPI;
import io.vertx.mutiny.redis.client.Response;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class ReactiveRedisTest {
    @Test
    public void putAndGet() {
        Vertx vertx = Vertx.vertx();

        Redis redis = Redis.createClient(vertx, "redis://redispass@172.17.0.2:6379/1");
        RedisAPI redisApi = RedisAPI.api(redis);

        Uni<Response> setResponse = redisApi.set(List.of("key1", "value1"));
        Uni<Response> getResponse = setResponse.flatMap(r -> redisApi.get("key1"));

        Response actualGetResponse = getResponse
                .await()
                .indefinitely();

        assertThat(actualGetResponse.toString())
                .isEqualTo("value1");
    }
}

Redisは、172.17.0.2でパスワード「redispass」で起動しているものとします。なお、バージョンは6.0.9を使用しました。

        Redis redis = Redis.createClient(vertx, "redis://redispass@172.17.0.2:6379/1");

あとは、SmallRye Mutiny向けに生成されたAPIを使ってテストを書いて確認。

    @Test
    public void putAndGet() {
        Vertx vertx = Vertx.vertx();

        Redis redis = Redis.createClient(vertx, "redis://redispass@172.17.0.2:6379/1");
        RedisAPI redisApi = RedisAPI.api(redis);

        Uni<Response> setResponse = redisApi.set(List.of("key1", "value1"));
        Uni<Response> getResponse = setResponse.flatMap(r -> redisApi.get("key1"));

        Response actualGetResponse = getResponse
                .await()
                .indefinitely();

        assertThat(actualGetResponse.toString())
                .isEqualTo("value1");
    }

すべて、SmalleRye Mutiny向けのパッケージです。Vertxすら、Mutiny向けのものがあるのに注意です。

import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.redis.client.Redis;
import io.vertx.mutiny.redis.client.RedisAPI;
import io.vertx.mutiny.redis.client.Response;

SmallRye Mutiny向けのVertxの生成元は、こちらですね。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/vertx-mutiny-clients/vertx-mutiny-core

テストは、これでパスするので最低限はOKということで。

Maven Compiler PluginとMaven Processor Pluginの使い分けは、オリジナルのライブラリからソースコードを自動生成するだけでは
なくて、src/main/javaに追加で自作のものがあったりする場合に選ぶのかな?とも思ったのですが、vertx-mutiny-core
構成を見る限りそうでもなさそうです…。こちらもMaven Processr Plugin。

https://github.com/smallrye/smallrye-reactive-utils/tree/1.2.2/vertx-mutiny-clients/vertx-mutiny-core

まあ、いいかな…?

まとめ

Vert.x API Generationについてちょっと調べてみたのと、自分でも他のライブラリからソースコードを生成する例を試して
みました。

これで、SmallRye Mutinyに対応されているやり方はなんとなくわかるでしょう。

というのと、あとでこのエントリを見れば思い出すでしょう…。

最後に、自前のsmallrye-mutiny-vertx-redis-clientを生成するpom.xmlの全体を2種類載せておきます。
マルチモジュール構成のまま載せているのは、ご愛嬌…。

Maven Compile Pluginを使うパターン。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>vertx-codegen-redis-example</artifactId>
        <groupId>org.littlewings</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>my-smallrye-mutiny-vertx-redis-client</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-redis-client</artifactId>
            <version>3.9.4</version>
        </dependency>

        <dependency>
            <groupId>io.smallrye.reactive</groupId>
            <artifactId>smallrye-mutiny-vertx-core</artifactId>
            <version>1.2.2</version>
        </dependency>

        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-codegen</artifactId>
            <classifier>processor</classifier>
            <version>3.9.4</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <includeGroupIds>io.vertx</includeGroupIds>
                    <includeArtifactIds>vertx-redis-client</includeArtifactIds>
                    <classifier>sources</classifier>
                    <includeTypes>jar</includeTypes>
                </configuration>
                <executions>
                    <execution>
                        <id>unpack-java</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includes>io/vertx/**/*.java</includes>
                            <excludes>**/impl/**/*.java</excludes>
                            <outputDirectory>${project.build.directory}/sources/java</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.build.directory}/sources/java</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <useIncrementalCompilation>false</useIncrementalCompilation>
                </configuration>
                <executions>
                    <execution>
                        <id>generate-mutiny-source</id>
                        <configuration>
                            <annotationProcessors>
                                <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
                            </annotationProcessors>
                            <compilerArgs>
                                <arg>-Acodegen.generators=mutiny</arg>
                            </compilerArgs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Maven Processor Pluginを使う場合。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>vertx-codegen-redis-example</artifactId>
        <groupId>org.littlewings</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>my-smallrye-mutiny-vertx-redis-client</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-redis-client</artifactId>
            <version>3.9.4</version>
        </dependency>

        <dependency>
            <groupId>io.smallrye.reactive</groupId>
            <artifactId>smallrye-mutiny-vertx-core</artifactId>
            <version>1.2.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <configuration>
                    <includeGroupIds>io.vertx</includeGroupIds>
                    <includeArtifactIds>vertx-redis-client</includeArtifactIds>
                    <classifier>sources</classifier>
                    <includeTypes>jar</includeTypes>
                </configuration>
                <executions>
                    <execution>
                        <id>unpack-java</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includes>io/vertx/**/*.java</includes>
                            <excludes>**/impl/**/*.java</excludes>
                            <outputDirectory>${project.build.directory}/sources/java</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.bsc.maven</groupId>
                <artifactId>maven-processor-plugin</artifactId>
                <version>4.4-jdk8</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.SimpleFormatter.format>%4$s: %3$s - %5$s %6$s%n</java.util.logging.SimpleFormatter.format>
                        <mvel2.disable.jit>true</mvel2.disable.jit>
                    </systemProperties>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceDirectory>${project.build.directory}/sources/java</sourceDirectory>
                            <processors>
                                <processor>io.vertx.codegen.CodeGenProcessor</processor>
                            </processors>
                            <optionMap>
                                <codegen.generators>mutiny</codegen.generators>
                            </optionMap>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>