CLOVER🍀

That was when it all began.

Eclipse MicroProfile Rest Clientを試す

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

Eclipse MicroProfileに含まれている、Rest Clientをちょっと見ておこうかなと。

今まで何回か別のテーマを扱っている時に使っているのですが、完全に雰囲気で使っていたのでこの機会に、と。

Eclipse MicroProfile Rest Client

Eclipse MicroProfile Rest Clientは、Eclipse MicroProfileのプロジェクトのひとつです。

eclipse/microprofile-rest-client / MicroProfile Rest Client

Eclipse MicroProfile 6.1にはRest Client 3.0が含まれています。

ドキュメントページはなく、GitHubリポジトリーと

GitHub - eclipse/microprofile-rest-client: MicroProfile Rest Client

Releaseから参照可能な

Release MicroProfile Rest Client 3.0 · eclipse/microprofile-rest-client · GitHub

仕様書とAPIリファレンスが主なリソースになります。

Rest Client for MicroProfile

MicroProfile Rest Client API

Rest Clientがどういうものかというと、GitHubリポジトリーのREADME.mdを見るのがわかりやすいと思うのですが、
Jakarta RESTful Web Services(JAX-RS)のアノテーションを使ったインターフェースを定義して

 @Path("/movies")
 public interface MovieReviewService {
     @GET
     Set<Movie> getAllMovies();

     @GET
     @Path("/{movieId}/reviews")
     Set<Review> getAllReviews( @PathParam("movieId") String movieId );

     @GET
     @Path("/{movieId}/reviews/{reviewId}")
     Review getReview( @PathParam("movieId") String movieId, @PathParam("reviewId") String reviewId );

     @POST
     @Path("/{movieId}/reviews")
     String submitReview( @PathParam("movieId") String movieId, Review review );

     @PUT
     @Path("/{movieId}/reviews/{reviewId}")
     Review updateReview( @PathParam("movieId") String movieId, @PathParam("reviewId") String reviewId, Review review );
 }

このインターフェースに対するクライアントを生成することができます。

URI apiUri = new URI("http://localhost:9080/movieReviewService");
MovieReviewService reviewSvc = RestClientBuilder.newBuilder()
            .baseUri(apiUri)
            .build(MovieReviewService.class);
Review review = new Review(3 /* stars */, "This was a delightful comedy, but not terribly realistic.");
reviewSvc.submitReview( movieId, review );

インターフェースは基本的にサーバー側と同じものを使い、ヘッダーは@HeaderParamを使うようになりますが、クライアント側の
宣言としてだけ入れたい場合は@ClientHeaderParamヘッダーを使うことになるようです。

MicroProfile Rest Client Definition Examples / Specifying Additional Client Headers

これには動的な値も指定可能ですが、インターフェース内のデフォルトメソッドか他のクラスのstaticメソッドを指定することに
なるようです。

また、CDIが使える場合は@RegisterRestClientアノテーションを付与したインターフェースに対してRest Clientを生成して

package com.mycompany.remoteServices;

@RegisterRestClient(baseUri="http://someHost/someContextRoot")
public interface MyServiceClient {
    @GET
    @Path("/greet")
    Response greet();
}

CDI管理Beanとしてインジェクションすることも可能なようです。

@ApplicationScoped
public class MyService {
    @Inject
    @RestClient
    private MyServiceClient client;
}

MicroProfile Rest Client CDI Support

さらにCDIが使える場合は、Eclipse MicroProfile Configを使ってRest Clientの設定もできるようです。

MicroProfile Rest Client CDI Support / Support for MicroProfile Config

リクエストするURL、タイムアウト、リダイレクトへの追従、プロキシ設定などが可能なようです。

その他、FilterやBodyWriter、Converter、InterceptorといったRest Client用のProviderなどの記載もあったりします。

MicroProfile Rest Client Provider Registration

Rest Clientの実装としては、以下の4つです。

  • Apache CXF
  • Open Liberty
  • RESTEasy
  • Jersey

今回はRESTEasy単体、それからWildFlyを使って試してみたいと思います。

RESTEasyでのドキュメントはこちらですね。

Chapter 51. MicroProfile Rest Client

ソースコードは、RESTEasy本体とは別のリポジトリーにあるようです。

GitHub - resteasy/resteasy-microprofile: RESTEasy MicroProfile

お題

お題は足し算にします。

以下のようなJAX-RSリソース定義に対して

@Path("calc")
public interface CalcResource {
    @GET
    @Path("plus")
    @Produces(MediaType.APPLICATION_JSON)
    CalcResult plus(@QueryParam("a") int a, @QueryParam("b") int b);

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    CalcResult plus(CalcRequest calcRequest);
}


public class CalcRequest {
    private int a;
    private int b;

    // getter/setterは省略
}


public class CalcResult {
    private int result;

    // getter/setterは省略
}

以下のパターンでEclipse MicroProfile Rest Clientを使うことを考えてみます。

環境

今回の環境は、こちら。

$ java --version
openjdk 21.0.1 2023-10-17
OpenJDK Runtime Environment (build 21.0.1+12-Ubuntu-222.04)
OpenJDK 64-Bit Server VM (build 21.0.1+12-Ubuntu-222.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 21.0.1, vendor: Private Build, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-91-generic", arch: "amd64", family: "unix"

WildFlyも使いますが、バージョンは30.0.1.Finalとします。

Java SE環境(CDIなし)でEclipse MicroProfile Rest Clientを試す

まずは、Java SE環境でEclipse MicroProfile Rest Clientを試します。ここでCDIを使うパターンを試さないのは、Java SE環境でCDI
使うことはそんなにないだろうと思うからですね…。

Maven依存関係など。

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jboss.resteasy.microprofile</groupId>
            <artifactId>microprofile-rest-client</artifactId>
            <version>2.1.5.Final</version>
        </dependency>
        <dependency>
            <groupId>io.smallrye.config</groupId>
            <artifactId>smallrye-config</artifactId>
            <version>3.4.4</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>6.2.7.Final</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.25.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-undertow</artifactId>
            <version>6.2.7.Final</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

使用するのは、microprofile-rest-clientです。

        <dependency>
            <groupId>org.jboss.resteasy.microprofile</groupId>
            <artifactId>microprofile-rest-client</artifactId>
            <version>2.1.5.Final</version>
        </dependency>

また、実行にはEclipse MicroProfile Configの実装も必要です。今回は、SmallRye Configを使いました。

        <dependency>
            <groupId>io.smallrye.config</groupId>
            <artifactId>smallrye-config</artifactId>
            <version>3.4.4</version>
        </dependency>

resteasy-jackson2-providerはJSONを扱うためで、それ以外はテスト用ですね。テストではUndertowを使ってテスト用のJAX-RSサーバーの
ソースコードを書きます。

Rest Clientで使うインターフェースを定義。

src/main/java/org/littlewings/resteasy/client/CalcResource.java

package org.littlewings.resteasy.client;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;

@Path("calc")
public interface CalcResource {
    @GET
    @Path("plus")
    @Produces(MediaType.APPLICATION_JSON)
    CalcResult plus(@QueryParam("a") int a, @QueryParam("b") int b);

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    CalcResult plus(CalcRequest calcRequest);
}

リクエストをJSONで送信する場合のクラス。

src/main/java/org/littlewings/resteasy/client/CalcRequest.java

package org.littlewings.resteasy.client;

public class CalcRequest {
    private int a;
    private int b;

    public static CalcRequest create(int a, int b) {
        CalcRequest calcRequest = new CalcRequest();
        calcRequest.a = a;
        calcRequest.b = b;
        return calcRequest;
    }

    // getter/setterは省略
}

計算結果。

src/main/java/org/littlewings/resteasy/client/CalcResult.java

package org.littlewings.resteasy.client;

public class CalcResult {
    private int result;

    public static CalcResult create(int result) {
        CalcResult calcResult = new CalcResult();
        calcResult.result = result;
        return calcResult;
    }

    // getter/setterは省略
}

では、こちらを使うテストコードで確認していくのですが、アクセスするHTTPサーバーが必要です。

これはRESTEasyとUndertowの組み合わせで作成。インターフェースは、Rest Client用に定義したものをそのまま実装しました。

src/test/java/org/littlewings/resteasy/client/TestServer.java

package org.littlewings.resteasy.client;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;

import java.util.Set;

public class TestServer {
    private UndertowJaxrsServer server;

    protected TestServer(UndertowJaxrsServer server) {
        this.server = server;
    }

    public static TestServer start(int port) {
        UndertowJaxrsServer server = new UndertowJaxrsServer();
        server.setPort(port);
        server.deploy(JaxrsActivator.class);
        server.start();

        return new TestServer(server);
    }

    public void stop() {
        server.stop();
    }

    @ApplicationPath("")
    public static class JaxrsActivator extends Application {
        @Override
        public Set<Class<?>> getClasses() {
            return Set.of(CalcResourceServer.class);
        }
    }

    public static class CalcResourceServer implements CalcResource {
        @Override
        public CalcResult plus(int a, int b) {
            return CalcResult.create(a + b);
        }

        @Override
        public CalcResult plus(CalcRequest calcRequest) {
            return CalcResult.create(calcRequest.getA() + calcRequest.getB());
        }
    }
}

そして、Rest Clientを使ったテストコード。

src/test/java/org/littlewings/resteasy/client/MicroProfileRestClientTest.java

package org.littlewings.resteasy.client;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.junit.jupiter.api.Test;

import java.net.URI;

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

class MicroProfileRestClientTest {
    @Test
    void get() {
        TestServer server = TestServer.start(8090);

        CalcResource calcResource =
                RestClientBuilder
                        .newBuilder()
                        .baseUri(URI.create("http://localhost:8090"))
                        .build(CalcResource.class);

        CalcResult result = calcResource.plus(5, 3);

        assertThat(result.getResult()).isEqualTo(8);

        server.stop();
    }

    @Test
    void post() {
        TestServer server = TestServer.start(8090);

        CalcResource calcResource =
                RestClientBuilder
                        .newBuilder()
                        .baseUri(URI.create("http://localhost:8090"))
                        .build(CalcResource.class);

        CalcResult result = calcResource.plus(CalcRequest.create(2, 7));

        assertThat(result.getResult()).isEqualTo(9);

        server.stop();
    }
}

およそ使い方は見ればわかりますが、RestClientBuilderを使い、作成したインターフェースを指定してbuildしてインターフェースに
対するインタンスを取得します。
あとは、エンドポイントに対応するメソッドを呼び出すだけですね。

        // GET
        CalcResource calcResource =
                RestClientBuilder
                        .newBuilder()
                        .baseUri(URI.create("http://localhost:8090"))
                        .build(CalcResource.class);

        CalcResult result = calcResource.plus(5, 3);


        // POST
        CalcResource calcResource =
                RestClientBuilder
                        .newBuilder()
                        .baseUri(URI.create("http://localhost:8090"))
                        .build(CalcResource.class);

        CalcResult result = calcResource.plus(CalcRequest.create(2, 7));

MicroProfile Rest Client Programmatic Lookup

とても簡単ですね。

Jakarta EE 10環境(WildFly上)で実行

次は、Jakarta EE 10環境(WildFly上)で実行してみます。実際に使う時は、Jakarta EEサーバー上で使うことの方が多いでしょう。

WildFlyのWeb Profile(standalone.xml)にはMicroProfile Rest Client、MicroProfile Configのどちらも含まれているようなので、
そのままWeb Profileを使います。

WildFly 30 is released!

2つのWebアプリケーションを作成して、以下のような構成にしてみたいと思います。

flowchart LR
    クライアント --> |curl/HTTP| A
    subgraph WildFly
    A[JAX-RS Server/API-A]
    A --> |MicroProfile REST Client/HTTP| B[JAX-RS Server/API-B]
    end

お題が足し算なのは変わりません。

使用するWildFlyのバージョンはこちら。

$ bin/standalone.sh --version
=========================================================================

  JBoss Bootstrap Environment

  JBOSS_HOME: /opt/wildfly

  JAVA: /opt/java/openjdk/bin/java

  JAVA_OPTS:  -Djdk.serialFilter="maxbytes=10485760;maxdepth=128;maxarray=100000;maxrefs=300000" -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true  --add-exports=java.desktop/sun.awt=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.url.ldaps=ALL-UNNAMED --add-exports=jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED -Djava.security.manager=allow

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

14:06:39,001 INFO  [org.jboss.modules] (main) JBoss Modules version 2.1.2.Final
WildFly Full 30.0.1.Final (WildFly Core 22.0.2.Final)

起動コマンドは以下です。

$ bin/standalone.sh \
    -Djboss.bind.address=0.0.0.0 \
    -Djboss.bind.address.management=0.0.0.0 \

まずは、奥にあるapi-bに方から。

Maven依存関係。

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>10.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>ROOT</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.4.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

JAX-RS有効化。

src/main/java/org/littlewings/wildfly/client/b/JaxrsActivator.java

package org.littlewings.wildfly.client.b;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("")
public class JaxrsActivator extends Application {
}

JAX-RSリソースクラス。

src/main/java/org/littlewings/wildfly/client/b/CalcResource.java

package org.littlewings.wildfly.client.b;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;

@Path("calc")
public class CalcResource {
    @GET
    @Path("plus")
    @Produces(MediaType.APPLICATION_JSON)
    public CalcResult plus(@QueryParam("a") int a, @QueryParam("b") int b) {
        return CalcResult.create(a + b);
    }

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public CalcResult plus(CalcRequest calcRequest) {
        return CalcResult.create(calcRequest.getA() + calcRequest.getB());
    }
}

CalcRequestCalcResultの定義はJava SE環境の時と同じなので省略します。

次に、こちらにアクセスするapi-a側を作成します。

Maven依存関係。

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.eclipse.microprofile</groupId>
                <artifactId>microprofile</artifactId>
                <version>6.0</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>10.0.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.eclipse.microprofile.rest.client</groupId>
            <artifactId>microprofile-rest-client-api</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>ROOT</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.4.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

Eclipse MicroProfile Rest Clientの依存関係は、APIのみですね。

        <dependency>
            <groupId>org.eclipse.microprofile.rest.client</groupId>
            <artifactId>microprofile-rest-client-api</artifactId>
            <scope>provided</scope>
        </dependency>

JAX-RS有効化。

src/main/java/org/littlewings/wildfly/client/a/JaxrsActivator.java

package org.littlewings.wildfly.client.a;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("")
public class JaxrsActivator extends Application {
}

Rest Client用のインターフェース。

src/main/java/org/littlewings/wildfly/client/a/CalcClient.java

package org.littlewings.wildfly.client.a;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("calc")
@RegisterRestClient
public interface CalcClient {
    @GET
    @Path("plus")
    @Produces(MediaType.APPLICATION_JSON)
    CalcResult plus(@QueryParam("a") int a, @QueryParam("b") int b);

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    CalcResult plus(CalcRequest calcRequest);
}

先ほどと違うのは、@RegisterRestClientアノテーションを付与していることですね。

CalcRequestCalcResultはやっぱり先ほどと同じなので省略します。

そして、Rest Clientを使うJAX-RSリソースクラス。

src/main/java/org/littlewings/wildfly/client/a/ProxyResource.java

package org.littlewings.wildfly.client.a;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import java.util.Map;

@ApplicationScoped
@Path("proxy")
public class ProxyResource {
    @Inject
    @RestClient
    private CalcClient calcClient;

    @GET
    @Path("plus")
    @Produces(MediaType.TEXT_PLAIN)
    public int plus(@QueryParam("a") int a, @QueryParam("b") int b) {
        return calcClient.plus(a, b).getResult();
    }

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, Integer> plus(Map<String, Integer> request) {
        CalcResult result = calcClient.plus(CalcRequest.create(request.get("a"), request.get("b")));
        return Map.of("result", result.getResult());
    }
}

@RegisterRestClientアノテーションを付与していることで、CDI管理BeanとしてRest Clientのインスタンスをインジェクションすることが
できます。

    @Inject
    @RestClient
    private CalcClient calcClient;

MicroProfile Rest Client CDI Support

ただ、これだとアクセス先が決まりません。

今回は、Eclipse MicroProfile Configを使って設定します。

src/main/resources/META-INF/microprofile-config.properties

org.littlewings.wildfly.client.a.CalcClient/mp-rest/url=http://172.17.0.3:8080

[インターフェース名]//mp-rest/[プロパティ名]で指定できます。設定可能なプロパティは、以下に記載されています。

MicroProfile Rest Client CDI Support / Support for MicroProfile Config

その他の方法としては、baseUriについては@RegisterRestClientアノテーションで設定することもできます。

@RegisterRestClient(baseUri="http://someHost/someContextRoot")
public interface MyServiceClient {
    @GET
    @Path("/greet")
    Response greet();
}

これで準備ができたので、それぞれパッケージングしてデプロイしておきます。

$ mvn package

確認。

## GET
$ curl '172.17.0.2:8080/proxy/plus?a=5&b=3'
8


## POST
$ curl -XPOST -H 'Content-Type: application/json' 172.17.0.2:8080/proxy/plus -d '{"a": 2, "b": 3}'
{"result":5}

OKですね。

これで、ざっくり確認できたと思います。

レスポンスヘッダーを確認したい

ところで、ドキュメントを見ているとリクエストヘッダーに関する話はあるのですが、レスポンスヘッダーについて触れられていません。

Rest Clientで使うインターフェースの定義で、以下のようにメソッドの戻り値をjakarta.ws.rs.core.Responseにする案もあるのかなと
思ったのですが。

@Path("calc")
@RegisterRestClient
public interface CalcClient2 {
    @GET
    @Path("plus")
    @Produces(MediaType.APPLICATION_JSON)
    Response plus(@QueryParam("a") int a, @QueryParam("b") int b);

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    Response plus(CalcRequest calcRequest);
}

こうすると、レスポンスボディはResponse#getEntityObject型として取得するか、Response#readEntityで特定の型として取得する
ことになります。

Rest Clientを使う側のイメージはこんな感じです。

@ApplicationScoped
@Path("proxy2")
public class ProxyResource2 {
    @Inject
    @RestClient
    private CalcClient2 calcClient2;

    @GET
    @Path("plus")
    @Produces(MediaType.TEXT_PLAIN)
    public int plus(@QueryParam("a") int a, @QueryParam("b") int b) {
        try (Response response = calcClient2.plus(a, b)) {
            return response.readEntity(CalcResult.class).getResult();
        }
    }

    @POST
    @Path("plus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, Integer> plus(Map<String, Integer> request) {
        try (Response response = calcClient2.plus(CalcRequest.create(request.get("a"), request.get("b")))) {
            CalcResult result = response.readEntity(CalcResult.class);
            return Map.of("result", result.getResult());
        }
    }
}

これはこれでレスポンスボディもレスポンスヘッダーも扱えますが、レスポンスボディの型はインターフェース定義からは
わからなくなってしまいます。

まあ、仕方ないかなと…。

おわりに

Eclipse MicroProfile Rest Clientを試してみました。

使い方はだいたいわかりましたが、HTTPヘッダーまわりの扱いがちょっと小回りが効かなさそうなので、ハマることがあるのかなと
思ったり。

とはいえ、JAX-RS Clientそのままよりは簡単に使えそうなので、使える環境の場合は基本的にはこちらでよいかなと思います。