14.7. MicroProfile Rest Client

14.7.1. 概要

MicroProfile Rest ClientはJAX-RS Client APIを拡張した機能を提供します。
MicroProfile Rest Clientの@RestClient、@RegisterRestClientのようなアノテーションを使って定義することで、容易にMicroProfile Rest Clientを作成できます。また、型や名前を誤った場合、アノテーションの定義により、より厳密に事前にコンパイルエラーとして検出することができます。

MicroProfile Rest Client アプリケーションはJAX-RS アプリケーションを呼び出す形になります。双方のアプリケーションは同一環境に配備しても別環境に配備しても実行可能です。

MicroProfile Rest Client 構成図

本製品では Eclipse MicroProfile Rest Client V1.1をサポートしています。JAX-RS のベースは2.0となります。
詳細は以下を参照してください。
Rest Client for MicroProfile

14.7.2. MicroProfile Rest Client アプリケーションの作成方法の違いによる特徴

MicroProfile Rest Client アプリケーションの作成方法には、CDIを使用する方法と、CDIを使用しない方法があります。また、一部機能にはどちらにも属さない作成方法があります。作成方法の違いによる特徴は以下のようになります。
14.7.2.1. CDIを使用する場合
アノテーションを使って定義することで、容易にアプリケーションを作成することができます。
また、JAX-RS アプリケーションに接続するためのベースURL/URIを、プロパティファイル(microprofile-config.properties)に記述します。そのため、warファイルを作成した後でも、再ビルドせずにベースURL/URIの変更が可能です。
14.7.2.2. CDIを使用しない場合
CDIを使用する場合と比べてコードは冗長になりますが、テスト中などCDIが使えない場面でも利用可能です。
しかし、JAX-RS アプリケーションに接続するためのベースURL/URIをソースに記述するため、ベースURL/URIの変更が必要な場合は再ビルドが必要です。

Memo
どちらにも属さないプロバイダクラス登録方法
独自に作成したプロバイダクラスの登録方法は、上記にそれぞれ存在しますが、それとは別に、プロバイダ構成ファイルにプロバイダクラス名を記載し、サービスローダによりロードさせる方法があります。これには登録方法をソースに記載しないという利点があります。上記それぞれでの登録方法の代わりに使用することができます。


14.7.3. 提供機能

MicroProfile Rest Client では、以下のような機能を提供します。
14.7.3.1. MicroProfile Rest Client の提供アノテーション
CDIを使用してアプリケーションを作成するために、次のアノテーションを提供します。

名称 必須 説明 アノテーション名
RegisterRestClient CDI管理Beanとして管理されるインタフェースに付与します。 @RegisterRestClient
RestClient CDIでインジェクトしたクラスをMicroProfile Rest Clientとしてインスタンス化します。 @RestClient
RegisterProvider - 独自に作成したプロバイダクラスを登録するようにMicroProfile Rest Client実装コードに指示します。
例)
@RegisterProvider(CustomExceptionMapper.class)
public interface RestClientInterface {
@RegisterProvider

上記以外に、JAX-RSで使用されるアノテーションも使用できます。そのうち、MicroProfile Rest Client でCDIを使用する場合に必須となるアノテーションは以下の通りです。

名称 説明 アノテーション名
Path 詳細はURIテンプレートを参照してください。 @Path
Inject CDI管理Beanをインジェクトします。 @Inject

CDIの対象とするために、スコープアノテーションかbeans.xmlのどちらかを定義する必要があります。
スコープアノテーションの種類は次の通りです。これらはJAX-RSでも使われています。
名称 スコープのライフサイクル アノテーション名
Dependent インジェクト先のBeanのライフサイクルに準ずる。デフォルト。 @Dependent
RequestScoped 1リクエストの間 @RequestScoped
SessionScoped 1セッションの間 @SessionScoped
ApplicationScoped アプリケーションの開始から終了まで @ApplicationScoped
ConversationScoped リクエスト以上セッション以下の範囲で、開始・終了をアプリケーションで明示する。 @ConversationScoped

beans.xmlはWEB-INF配下に配置します。
beans.xml 配置図

beans.xmlの記述内容は以下の通りです。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
</beans>
    
このうち、設定項目は bean-discovery-mode で、設定値は以下の通りです。
annotated を設定した場合、結局スコープアノテーションも付与する必要があります。
設定値 説明
annotated スコープアノテーションが付与されたクラスのみを CDI管理Beanとする。
all クラスパス上の全てのクラスをCDI Beanとする。スコープアノテーションがないクラスは@Dependentが付与されているとみなす。
none 全てのクラスを CDI Beanとしない。


Caution
上記でスコープアノテーションを付与する方法を採用した場合、次の注意点があります。
スコープアノテーションを省略した場合、@Dependentアノテーションがデフォルトとなります。
一方、beans.xmlがない場合、bean-discovery-mode属性のデフォルト値はannotatedとなります。annotatedはスコープアノテーションが付与されたクラスのみを CDI管理Beanとします。したがって、対象クラスをCDI管理Beanとしたい場合は、スコープアノテーションを明示する必要があります。


14.7.3.2. ベースURL/URI(※)の設定
MicroProfile Rest Client 設定項目一覧の[インターフェースの完全修飾名 + "/mp-rest/" + "url" または"uri"]を参照してください。
microprofile-config.properties はMicroProfile Configの機能を使うための設定ファイルです。microprofile-config.properties を使わずに設定する方法もあります。詳細は[MicroProfile Config]をご確認ください。
14.7.3.3. RestClientBuilder APIの提供メソッド
CDIを使用せずにアプリケーションを作成するために、次の機能を提供します。
なお、すべてRestClientBuilder API(org.eclipse.microprofile.rest.client.RestClientBuilder)のメソッドであり、戻り値はすべてRestClientBuilderになります。
RestClientBuilder APIの他のメソッドを実行する前に、まずnewBuilderメソッドを実行してインスタンスを生成してください。最後にbuildメソッドを実行し、その間にその他メソッドを実行してください。例はCDI未使用時の[ルートリソースクラスの作成]を参照してください。
メソッド名 必須 引数 説明
newBuilder - RestClientBuilder APIのインスタンスを生成します。
baseUrl(baseUri) ベースURL(URI)(※) JAX-RS アプリケーションにアクセスするためのベースURL(URI)(※)を指定します。CDIを使用する場合のmicroprofile-config.propertiesの設定に該当します。
register - プロバイダクラス 独自に作成したプロバイダクラスを指定します。CDIを使用する場合の@RegisterProviderアノテーションに該当します。
executorService - ExecutorServiceの独自実装クラス 非同期処理でExecutorServiceを独自実装する場合に指定します。
property - (String)プロパティ名,
(Object)値
新しいプロパティを設定したり、既存のプロパティ値を変更したりします。
build インターフェース 実装するインターフェースを指定し、クライアントをインスタンス化します。

CDIを使用しない場合でもJAX-RSで使用されるアノテーションを使用できます。必須は@Pathアノテーションのみです。
14.7.3.4. サービスローダの設定
独自に作成したプロバイダクラスを登録する方法として、上記で@RegisterProviderアノテーションを使う方法と、RestClientBuilder APIのregisterメソッドを使う方法を記載しましたが、代わりに下記の方法を取ることもできます。
下記の方法を使用する場合は、@RegisterProviderアノテーションやRestClientBuilder APIのregisterメソッドで同じプロバイダクラスを指定しないでください。
  1. org.eclipse.microprofile.rest.client.spi.RestClientBuilderListenerインターフェースを実装します。
  2. META-INF/services ディレクトリ配下に org.eclipse.microprofile.rest.client.spi.RestClientBuilderListener ファイル(プロバイダ構成ファイル)を作成します。
  3. 上記ファイルに、RestClientBuilderListenerインターフェース実装クラスの完全修飾クラス名を記載します。
    その際、文字コードをUTF-8にします。
後はアプリケーションを配備することで、サービスローダによりロードされます。
14.7.3.5. 独自例外Mapperクラスの作成
org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapperインターフェースを実装することで、JAX-RS アプリケーションからのレスポンスと例外クラスの対応付けを行うことができます。
ResponseExceptionMapperインターフェースには次のメソッドがあります。
メソッド名 引数 戻り値 説明
getPriority - int 独自例外Mapperクラスの優先順位を返します。
handles (int)HTTPステータスコード
(MultivaluedMap<String, Object>)ヘッダー
boolean レスポンスのHTTPステータスコードやヘッダーの内容により、独自例外Mapperクラスで扱うかどうかを返します。
toThrowable (Response)レスポンス 例外インスタンス handlesメソッドの条件に該当するレスポンスの内容に基づき、例外レスポンスを返します。

独自例外Mapperクラスの実装例を記載します。この例では、HTTPステータスコードに対応する例外インスタンスを返しています。
CustomUnknownUrlException、CustomConfrictDataExceptionは独自例外クラス、CustomBaseExceptionはその親クラスという例ですが、既存の例外クラスも指定できます。
@Provider
public class CustomExceptionMapper implements ResponseExceptionMapper<CustomBaseException> {
    @Override
    public boolean handles(int status, MultivaluedMap<String, Object> headers) {
        return status == 404 || status == 409;
    }
    
    @Override
    public CustomBaseException toThrowable(Response response) {
        switch (response) {
            case 404:
                return new CustomUnknownUrlException();
            case 409:
                return new CustomConfrictDataException();
        }
        return null;
    }
}
    

独自例外Mapperクラスは、例で@Providerアノテーションを使っていることからもわかる通り、独自のプロバイダクラスに該当します。登録方法は、[MicroProfile Rest Client の提供アノテーション]の@RegisterProviderアノテーション、[RestClientBuilder APIの提供メソッド]のregisterメソッド、[サービスローダの設定]のいずれかの方法で行ってください。
14.7.3.6. デフォルト例外Mapper
デフォルト例外Mapperは、レスポンスのHTTPステータスコードが400以上の場合にWebApplicationExceptionを返します。ただし、独自例外Mapperクラスが優先され、独自例外Mapperクラスで条件に一致しない場合にデフォルト例外Mapperが実行されます。
デフォルト例外Mapperはデフォルトで有効になっていますが、無効にする方法もあります。次のどちらかの方法を行ってください。
14.7.3.7. 非同期処理
Rest Clientインタフェースメソッドを非同期的に宣言することができます。標準のJAX-RSでは非同期処理のための別スレッド管理の仕組みがないため、スレッド管理機構としてConcurrency Utilities for Java EEを利用します。
非同期メソッドを宣言する場合、戻り値の型はjava.util.concurrent.CompletionStageを利用します。
public interface CustomAsyncClient {
    @GET
    public CompletionStage<Response> getAsync();
}
    
返却されたCompletionStageは、CompletableFuture型に変換することで、処理結果をget()メソッドで取得できます。型変換はCompletionStage#toCompletableFuture()メソッドを使用します。
get()メソッドは、非同期で実行されている処理が終わるまで呼び出し元のスレッドをブロックして待ちます。
CompletableFuture<Response> future = customAsyncClient.getAsync().toCompletableFuture();
Response res = future.get();
    
14.7.3.8. ※ベースURL/URIとは
MicroProfile Rest Client アプリケーションからJAX-RS アプリケーションにアクセスするために、MicroProfile Rest Client アプリケーションで、JAX-RS アプリケーションの基本となるURL/URIを指定します。この基本となるURL/URIのことをベースURL/URIと言います。
JAX-RS アプリケーションに "http://localhost/Hello/name_list/0001" のようなURLでアクセスする場合、"name_list" がJAX-RS アプリケーションクラスの@Pathアノテーションで定義されているとすると、ベースURL/URIは "http://localhost/Hello" になります。

14.7.4. XMLで通信するMicroProfile Rest Client アプリケーションの作成

ここでは、CDIを使用してアプリケーションを作成する方法と、CDIを使用せずにアプリケーションを作成する方法を紹介します。
[XMLで通信するRESTful Webサービスの作成]で作成されたJAX-RS アプリケーションにアクセスするMicroProfile Rest Client アプリケーションの作成方法について説明します。事前にJAX-RS アプリケーションを作成しておいてください。
14.7.4.1. MicroProfile Rest Client アプリケーション作成(CDI使用)
(サンプルプロジェクト RestClientXML.zip)
MicroProfile Rest Clientプロジェクトの作成
[RESTful Webサービスプロジェクトの作成] と同じ手順でプロジェクトを作成します。この例ではプロジェクト名を以下のように指定します。
項目
プロジェクト名 RestClientXML
Customerクラスのコピー
Developer上でRestSampleXMLプロジェクトのsample.resourceのCustomer.javaをパッケージごと、RestClientXMLプロジェクトのsrcフォルダにコピーします。
インターフェースの作成
RestClientXMLプロジェクトで右クリック > 新規 > インターフェースを選択し、パッケージsample.restclientに、RestClientInterfaceというインターフェースを作成します。
作成されたRestClientInterfaceインターフェースをエディタで開いて、以下の太字部分の内容を追加し、保存します。
package sample.restclient;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import sample.resource.Customer;

@RegisterRestClient
public interface RestClientInterface {
    public Customer getCustomerList(String id);
}
    
クラス名RestClientInterfaceを選択し、注釈プロパティービューでアノテーション@Pathを追加し、そのvalue属性の値を"/name_list"に設定します。value属性の値はJAX-RS アプリケーションでクラスに設定した@Pathのvalue属性の値と同じ内容を設定します。

RestClientInterfaceインターフェース 注釈プロパティビュー アノテーション追加

同様の方法で、getCustomerListメソッドにアノテーション@Produces(value = { "application/xml" })@GET@Path(value = "{id}")を追加し、パラメーターidにアノテーション@PathParam(value = "id")を追加します。
package sample.restclient;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import sample.resource.Customer;

@Path(value = "/name_list")
@RegisterRestClient
public interface RestClientInterface {
    @Produces(value = { "application/xml" })
    @GET
    @Path(value = "{id}")
    public Customer getCustomerList(@PathParam(value = "id") String id);
}
    
ルートリソースクラスの作成
RestClientXMLプロジェクトで右クリック > 新規 > クラスを選択し、パッケージsample.restclientに、RestClientResourceというクラスを作成します。
作成されたRestClientResourceクラスをエディタで開いて、以下の太字部分の内容を追加し、保存します。
package sample.restclient;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import sample.resource.Customer;

public class RestClientResource {
    
    @RestClient
    RestClientInterface itf;

    public Customer getCustomerList(String id) {
        Customer custom = itf.getCustomerList(id);
        return custom;
    }
}
    
RestClientInterfaceの場合と同様に、注釈プロパティービューを使い、以下のようにアノテーションを追加します。
package sample.restclient;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import sample.resource.Customer;

@Path("/client")
@RequestScoped
public class RestClientResource {
    
    @Inject
    @RestClient
    RestClientInterface itf;

    @Produces(value = { "application/xml" })
    @GET
    @Path(value = "{id}")
    public Customer getCustomerList(@PathParam(value = "id") String id) {
        Customer custom = itf.getCustomerList(id);
        return custom;
    }
}
    
web.xml にリソースクラスを指定
web.xmlに以下のコードを追加します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>RestClientXML</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>JAX-RS Servlet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.classnames</param-name>
      <param-value>sample.restclient.RestClientResource</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JAX-RS Servlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>
microprofile-config.propertiesの作成
RestClientXMLプロジェクトで右クリック > 新規 > フォルダーを選択し、srcフォルダー配下にMETA-INFの名称で作成します。フォルダー名と作成場所は固定です。
RestClientXMLプロジェクトで右クリック > 新規 > ファイルを選択し、src/META-INFフォルダー配下にmicroprofile-config.propertiesの名称で作成します。ファイル名と作成場所は固定です。

新規ファイル

microprofile-config_propertiesをエディタで開いて、以下の形式で1行追加します。
インターフェースの完全修飾名 + "/mp-rest/" + "url" または"uri"=JAX-RS アプリケーションのベースURL/URI

Caution
microprofile-config_propertiesへの記述はSourceタブで行ってください。Propertiesタブに記述すると、ベースURL/URIに意図せずエスケープ文字が追加され、正しいURL/URIとして認識されなくなります。

プロパティファイル

ここまでで、CDIを使用するMicroProfile Rest Client アプリケーションの作成が完了しました。
アプリケーションのデプロイ
作成したMicroProfile Rest Client アプリケーションをアーカイブしてサーバに配備します。「WARファイルを作成します」を参照してください。その際、Webプロジェクト名はRestClientXMLにします。
JAX-RS アプリケーションも同様にサーバに配備します。
アプリケーションの実行
MicroProfile Rest Client アプリケーションにWebブラウザでアクセスします。
http://localhost/{アプリケーション名}/client/{id}

http://localhost/RestClientXML/client/0001にアクセスすると、 id0001のコンシューマの氏名が表示されます。

idが0001のブラウザ表示結果


14.7.4.2. MicroProfile Rest Client アプリケーション作成(CDI未使用)
(サンプルプロジェクト RestClientXMLByBuilder.zip)
MicroProfile Rest Clientプロジェクトの作成
[RESTful Webサービスプロジェクトの作成] と同じ手順でプロジェクトを作成します。この例ではプロジェクト名を以下のように指定します。
項目
プロジェクト名 RestClientXMLByBuilder
Customerクラスのコピー
Developer上でRestSampleXMLプロジェクトのsample.resourceのCustomer.javaをパッケージごと、RestClientXMLByBuilderプロジェクトのsrcフォルダにコピーします。
インターフェースの作成
CDI使用時の[RestClientInterfaceインターフェースの作成]を参照してください。ただし、アノテーション@RegisterRestClientは追加しません。
ルートリソースクラスの作成
RestClientXMLByBuilderプロジェクトで右クリック > 新規 > クラスを選択し、パッケージsample.restclientに、RestClientResourceというクラスを作成します。
作成されたRestClientResourceクラスをエディタで開いて、以下の太字部分の内容を追加し、保存します。
URLにはJAX-RS アプリケーションのベースURLを指定します。
package sample.restclient;

import java.net.URL;

import org.eclipse.microprofile.rest.client.RestClientBuilder;

import sample.resource.Customer;

public class RestClientResource {
    public Customer getCustomerList(String id) {
        URL apUrl = null;
        try {
            apUrl = new URL("http://localhost/Hello");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        RestClientInterface itf = RestClientBuilder.newBuilder()
                                                   .baseUrl(apUrl)
                                                   .build(RestClientInterface.class);
        Customer custom = itf.getCustomerList(id);

        return custom;
    }
}
    
CDI使用時の[ルートリソースクラスの作成]を参照して、注釈プロパティービューを使い、アノテーションを追加してください。
ただし、@RequestScoped@Injectは追加しません。
package sample.restclient;

import java.net.URL;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.eclipse.microprofile.rest.client.RestClientBuilder;

import sample.resource.Customer;

@Path(value = "client")
public class RestClientResource {
    @Produces(value = { "application/xml" })
    @GET
    @Path(value = "{id}")
    public Customer getCustomerList(@PathParam(value = "id") String id) {
        URL apUrl = null;
        try {
            apUrl = new URL("http://localhost/Hello");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        RestClientInterface itf = RestClientBuilder.newBuilder()
                                                   .baseUrl(apUrl)
                                                   .build(RestClientInterface.class);
        Customer custom = itf.getCustomerList(id);

        return custom;
    }
}
    
web.xml にリソースクラスを指定
CDI使用時の[web.xml にリソースクラスを指定]を参照してください。

ここまでで、CDIを使用しないMicroProfile Rest Client アプリケーションの作成が完了しました。
アプリケーションのデプロイ
作成したMicroProfile Rest Client アプリケーションをアーカイブしてサーバに配備します。「WARファイルを作成します」を参照してください。その際、Webプロジェクト名はRestClientXMLByBuilderにします。
JAX-RS アプリケーションも同様にサーバに配備します。
アプリケーションの実行
MicroProfile Rest Client アプリケーションにWebブラウザでアクセスします。
http://localhost/{アプリケーション名}/client/{id}

http://localhost/RestClientXMLByBuilder/client/0001にアクセスすると、 id0001のコンシューマの氏名が表示されます。

idが0001のブラウザ表示結果


14.7.5. JSONで通信するMicroProfile Rest Client アプリケーションの作成

[JSONで通信するRESTful Webサービスの作成]で作成されたJAX-RS アプリケーションにアクセスするMicroProfile Rest Client アプリケーションの作成方法について説明します。事前にJAX-RS アプリケーションを作成しておいてください。

基本的に[JSONで通信するRESTful Webサービスの作成]と同じで、上記の[XMLで通信するMicroProfile Rest Client アプリケーションの作成]のRestClientInterfaceインターフェース、CustomerResourceクラスの @Produces("application/xml") を @Produces("application/json") に変更するたけで、JSONで通信するMicroProfile Rest Client アプリケーションに変更できます。

RestClientInterfaceインターフェース
package sample.restclient;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import sample.resource.Customer;

@Path(value = "/name_list")
@RegisterRestClient
public interface RestClientInterface {
    @Produces(value = { "application/json" })
    @GET
    @Path(value = "{id}")
    public Customer getCustomerList(@PathParam(value = "id") String id);
}
    
CustomerResourceクラス
package sample.restclient;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import sample.resource.Customer;

@Path("/client")
@RequestScoped
public class RestClientResource {
    
    @Inject
    @RestClient
    RestClientInterface itf;

    @Produces(value = { "application/json" })
    @GET
    @Path(value = "{id}")
    public Customer getCustomerList(@PathParam(value = "id") String id) {
        Customer custom = itf.getCustomerList(id);
        return custom;
    }
}